@cephalization/phoenix-insight 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +620 -0
  3. package/dist/agent/index.js +230 -0
  4. package/dist/cli.js +640 -0
  5. package/dist/commands/index.js +2 -0
  6. package/dist/commands/px-fetch-more-spans.js +98 -0
  7. package/dist/commands/px-fetch-more-trace.js +110 -0
  8. package/dist/config/index.js +165 -0
  9. package/dist/config/loader.js +141 -0
  10. package/dist/config/schema.js +53 -0
  11. package/dist/index.js +1 -0
  12. package/dist/modes/index.js +17 -0
  13. package/dist/modes/local.js +134 -0
  14. package/dist/modes/sandbox.js +121 -0
  15. package/dist/modes/types.js +1 -0
  16. package/dist/observability/index.js +65 -0
  17. package/dist/progress.js +209 -0
  18. package/dist/prompts/index.js +1 -0
  19. package/dist/prompts/system.js +30 -0
  20. package/dist/snapshot/client.js +74 -0
  21. package/dist/snapshot/context.js +332 -0
  22. package/dist/snapshot/datasets.js +68 -0
  23. package/dist/snapshot/experiments.js +135 -0
  24. package/dist/snapshot/index.js +262 -0
  25. package/dist/snapshot/projects.js +44 -0
  26. package/dist/snapshot/prompts.js +199 -0
  27. package/dist/snapshot/spans.js +80 -0
  28. package/dist/tsconfig.esm.tsbuildinfo +1 -0
  29. package/package.json +75 -0
  30. package/src/agent/index.ts +323 -0
  31. package/src/cli.ts +782 -0
  32. package/src/commands/index.ts +8 -0
  33. package/src/commands/px-fetch-more-spans.ts +174 -0
  34. package/src/commands/px-fetch-more-trace.ts +183 -0
  35. package/src/config/index.ts +225 -0
  36. package/src/config/loader.ts +173 -0
  37. package/src/config/schema.ts +66 -0
  38. package/src/index.ts +1 -0
  39. package/src/modes/index.ts +21 -0
  40. package/src/modes/local.ts +163 -0
  41. package/src/modes/sandbox.ts +144 -0
  42. package/src/modes/types.ts +31 -0
  43. package/src/observability/index.ts +90 -0
  44. package/src/progress.ts +239 -0
  45. package/src/prompts/index.ts +1 -0
  46. package/src/prompts/system.ts +31 -0
  47. package/src/snapshot/client.ts +129 -0
  48. package/src/snapshot/context.ts +462 -0
  49. package/src/snapshot/datasets.ts +132 -0
  50. package/src/snapshot/experiments.ts +246 -0
  51. package/src/snapshot/index.ts +403 -0
  52. package/src/snapshot/projects.ts +58 -0
  53. package/src/snapshot/prompts.ts +267 -0
  54. package/src/snapshot/spans.ts +142 -0
@@ -0,0 +1,267 @@
1
+ import type { PhoenixClient } from "@arizeai/phoenix-client";
2
+ import type { ExecutionMode } from "../modes/types.js";
3
+ import { withErrorHandling, extractData } from "./client.js";
4
+
5
+ interface FetchPromptsOptions {
6
+ limit?: number;
7
+ }
8
+
9
+ /**
10
+ * Converts an array to JSONL format
11
+ */
12
+ function toJSONL(items: unknown[]): string {
13
+ if (items.length === 0) {
14
+ return "";
15
+ }
16
+ return items.map((item) => JSON.stringify(item)).join("\n");
17
+ }
18
+
19
+ /**
20
+ * Converts a prompt template to markdown format
21
+ */
22
+ function templateToMarkdown(template: any, templateFormat: string): string {
23
+ // Handle string template
24
+ if (templateFormat === "STRING") {
25
+ if (typeof template === "string") {
26
+ return template;
27
+ }
28
+ // It might be wrapped in an object with type
29
+ if (template?.template && typeof template.template === "string") {
30
+ return template.template;
31
+ }
32
+ return JSON.stringify(template, null, 2);
33
+ }
34
+
35
+ // Handle chat template
36
+ const messages = template?.messages || [];
37
+ const lines: string[] = ["# Chat Template", ""];
38
+
39
+ for (const message of messages) {
40
+ lines.push(`## ${message.role}`);
41
+ lines.push("");
42
+
43
+ if (typeof message.content === "string") {
44
+ lines.push(message.content);
45
+ } else if (Array.isArray(message.content)) {
46
+ // Handle multi-part content
47
+ for (const part of message.content) {
48
+ if (part.type === "text" && part.text) {
49
+ lines.push(part.text);
50
+ } else {
51
+ // For non-text parts, show as JSON
52
+ lines.push("```json");
53
+ lines.push(JSON.stringify(part, null, 2));
54
+ lines.push("```");
55
+ }
56
+ }
57
+ } else {
58
+ // If content is not string or array, show as JSON
59
+ lines.push("```json");
60
+ lines.push(JSON.stringify(message.content, null, 2));
61
+ lines.push("```");
62
+ }
63
+ lines.push("");
64
+ }
65
+
66
+ return lines.join("\n");
67
+ }
68
+
69
+ /**
70
+ * Creates a markdown representation of a prompt version
71
+ */
72
+ function createVersionMarkdown(version: any): string {
73
+ const lines: string[] = [];
74
+
75
+ // Add metadata header
76
+ lines.push("---");
77
+ lines.push(`id: ${version.id}`);
78
+ if (version.model_name) lines.push(`model_name: ${version.model_name}`);
79
+ if (version.model_provider)
80
+ lines.push(`model_provider: ${version.model_provider}`);
81
+ if (version.template_format)
82
+ lines.push(`template_format: ${version.template_format}`);
83
+ if (version.description) lines.push(`description: ${version.description}`);
84
+ lines.push("---");
85
+ lines.push("");
86
+
87
+ // Add template content
88
+ if (version.template) {
89
+ lines.push(
90
+ templateToMarkdown(version.template, version.template_format || "STRING")
91
+ );
92
+ lines.push("");
93
+ }
94
+
95
+ // Add invocation parameters if present
96
+ if (version.invocation_parameters) {
97
+ lines.push("## Invocation Parameters");
98
+ lines.push("");
99
+ lines.push("```json");
100
+ lines.push(JSON.stringify(version.invocation_parameters, null, 2));
101
+ lines.push("```");
102
+ lines.push("");
103
+ }
104
+
105
+ // Add tools if present
106
+ if (version.tools) {
107
+ lines.push("## Tools");
108
+ lines.push("");
109
+ lines.push("```json");
110
+ lines.push(JSON.stringify(version.tools, null, 2));
111
+ lines.push("```");
112
+ lines.push("");
113
+ }
114
+
115
+ // Add response format if present
116
+ if (version.response_format) {
117
+ lines.push("## Response Format");
118
+ lines.push("");
119
+ lines.push("```json");
120
+ lines.push(JSON.stringify(version.response_format, null, 2));
121
+ lines.push("```");
122
+ lines.push("");
123
+ }
124
+
125
+ return lines.join("\n");
126
+ }
127
+
128
+ /**
129
+ * Fetches all prompts and their versions from Phoenix
130
+ */
131
+ export async function fetchPrompts(
132
+ client: PhoenixClient,
133
+ mode: ExecutionMode,
134
+ options: FetchPromptsOptions = {}
135
+ ): Promise<void> {
136
+ const { limit = 100 } = options;
137
+
138
+ // Fetch all prompts with pagination
139
+ const prompts: any[] = [];
140
+ let cursor: string | null = null;
141
+
142
+ while (prompts.length < limit) {
143
+ const query: Record<string, unknown> = {
144
+ limit: Math.min(limit - prompts.length, 100),
145
+ };
146
+ if (cursor) {
147
+ query.cursor = cursor;
148
+ }
149
+
150
+ const response = await withErrorHandling(
151
+ () => client.GET("/v1/prompts", { params: { query } }),
152
+ "fetching prompts"
153
+ );
154
+
155
+ const data = extractData(response);
156
+ prompts.push(...data.data);
157
+ cursor = data.next_cursor;
158
+
159
+ // Stop if no more data
160
+ if (!cursor || data.data.length === 0) {
161
+ break;
162
+ }
163
+ }
164
+
165
+ // Write prompts index
166
+ await mode.writeFile("/phoenix/prompts/index.jsonl", toJSONL(prompts));
167
+
168
+ // Fetch versions for each prompt
169
+ for (const prompt of prompts) {
170
+ const safePromptName = prompt.name.replace(/[^a-zA-Z0-9-_]/g, "_");
171
+
172
+ // Write prompt metadata
173
+ await mode.writeFile(
174
+ `/phoenix/prompts/${safePromptName}/metadata.json`,
175
+ JSON.stringify(
176
+ {
177
+ id: prompt.id,
178
+ name: prompt.name,
179
+ description: prompt.description || null,
180
+ metadata: prompt.metadata || {},
181
+ source_prompt_id: prompt.source_prompt_id || null,
182
+ snapshot_timestamp: new Date().toISOString(),
183
+ },
184
+ null,
185
+ 2
186
+ )
187
+ );
188
+
189
+ // Fetch all versions for this prompt
190
+ const versions: any[] = [];
191
+ let versionCursor: string | null = null;
192
+
193
+ while (true) {
194
+ const versionQuery: Record<string, unknown> = {
195
+ limit: 100,
196
+ };
197
+ if (versionCursor) {
198
+ versionQuery.cursor = versionCursor;
199
+ }
200
+
201
+ const versionsResponse = await withErrorHandling(
202
+ () =>
203
+ client.GET("/v1/prompts/{prompt_identifier}/versions", {
204
+ params: {
205
+ path: { prompt_identifier: prompt.id },
206
+ query: versionQuery,
207
+ },
208
+ }),
209
+ `fetching versions for prompt ${prompt.name}`
210
+ );
211
+
212
+ const versionsData = extractData(versionsResponse);
213
+ versions.push(...versionsData.data);
214
+ versionCursor = versionsData.next_cursor;
215
+
216
+ // Stop if no more data
217
+ if (!versionCursor || versionsData.data.length === 0) {
218
+ break;
219
+ }
220
+ }
221
+
222
+ // Write versions index as JSONL
223
+ await mode.writeFile(
224
+ `/phoenix/prompts/${safePromptName}/versions/index.jsonl`,
225
+ toJSONL(versions)
226
+ );
227
+
228
+ // Write each version as markdown
229
+ for (const version of versions) {
230
+ const versionId = version.id.replace(/[^a-zA-Z0-9-_]/g, "_");
231
+ const markdownContent = createVersionMarkdown(version);
232
+
233
+ await mode.writeFile(
234
+ `/phoenix/prompts/${safePromptName}/versions/${versionId}.md`,
235
+ markdownContent
236
+ );
237
+ }
238
+
239
+ // Fetch and save the latest version separately for convenience
240
+ try {
241
+ const latestResponse = await withErrorHandling(
242
+ () =>
243
+ client.GET("/v1/prompts/{prompt_identifier}/latest", {
244
+ params: {
245
+ path: { prompt_identifier: prompt.id },
246
+ },
247
+ }),
248
+ `fetching latest version for prompt ${prompt.name}`
249
+ );
250
+
251
+ const latestData = extractData(latestResponse);
252
+ if (latestData.data) {
253
+ const latestMarkdownContent = createVersionMarkdown(latestData.data);
254
+
255
+ await mode.writeFile(
256
+ `/phoenix/prompts/${safePromptName}/latest.md`,
257
+ latestMarkdownContent
258
+ );
259
+ }
260
+ } catch (error) {
261
+ // If there's no latest version, that's okay - just skip it
262
+ console.warn(
263
+ `No latest version available for prompt ${prompt.name}: ${error instanceof Error ? error.message : String(error)}`
264
+ );
265
+ }
266
+ }
267
+ }
@@ -0,0 +1,142 @@
1
+ import type { PhoenixClient } from "@arizeai/phoenix-client";
2
+ import type { ExecutionMode } from "../modes/types.js";
3
+ import { withErrorHandling } from "./client.js";
4
+
5
+ export interface SnapshotSpansOptions {
6
+ /** Inclusive lower bound time for filtering spans */
7
+ startTime?: Date | string | null;
8
+ /** Exclusive upper bound time for filtering spans */
9
+ endTime?: Date | string | null;
10
+ /** Maximum number of spans to fetch per project (default: 1000) */
11
+ spansPerProject?: number;
12
+ }
13
+
14
+ interface SpanData {
15
+ id: string;
16
+ name: string;
17
+ context: {
18
+ trace_id: string;
19
+ span_id: string;
20
+ };
21
+ span_kind: string;
22
+ parent_id: string | null;
23
+ start_time: string;
24
+ end_time: string;
25
+ status_code: string;
26
+ status_message: string;
27
+ attributes: Record<string, unknown>;
28
+ events: Array<unknown>;
29
+ }
30
+
31
+ interface ProjectMetadata {
32
+ name: string;
33
+ }
34
+
35
+ /**
36
+ * Fetches spans for all projects and writes them to the snapshot
37
+ *
38
+ * @param client - Phoenix client instance
39
+ * @param mode - Execution mode for file operations
40
+ * @param options - Options for filtering and limiting spans
41
+ */
42
+ export async function snapshotSpans(
43
+ client: PhoenixClient,
44
+ mode: ExecutionMode,
45
+ options: SnapshotSpansOptions = {}
46
+ ): Promise<void> {
47
+ const { startTime, endTime, spansPerProject = 1000 } = options;
48
+
49
+ // Read projects index to get project names
50
+ const projectsIndexContent = await mode.exec(
51
+ "cat /phoenix/projects/index.jsonl"
52
+ );
53
+ if (!projectsIndexContent.stdout) {
54
+ // No projects, nothing to do
55
+ return;
56
+ }
57
+
58
+ const projectNames = projectsIndexContent.stdout
59
+ .trim()
60
+ .split("\n")
61
+ .filter((line) => line.length > 0)
62
+ .map((line) => {
63
+ const project = JSON.parse(line) as ProjectMetadata;
64
+ return project.name;
65
+ });
66
+
67
+ // Fetch spans for each project
68
+ for (const projectName of projectNames) {
69
+ await withErrorHandling(async () => {
70
+ const spans: SpanData[] = [];
71
+ let cursor: string | null = null;
72
+ let totalFetched = 0;
73
+
74
+ // Paginate through spans until we reach the limit or no more data
75
+ while (totalFetched < spansPerProject) {
76
+ const query: Record<string, any> = {
77
+ limit: Math.min(100, spansPerProject - totalFetched), // Fetch in chunks of 100
78
+ };
79
+
80
+ if (cursor) {
81
+ query.cursor = cursor;
82
+ }
83
+
84
+ if (startTime) {
85
+ query.start_time =
86
+ startTime instanceof Date ? startTime.toISOString() : startTime;
87
+ }
88
+
89
+ if (endTime) {
90
+ query.end_time =
91
+ endTime instanceof Date ? endTime.toISOString() : endTime;
92
+ }
93
+
94
+ const response = await client.GET(
95
+ "/v1/projects/{project_identifier}/spans",
96
+ {
97
+ params: {
98
+ path: {
99
+ project_identifier: projectName,
100
+ },
101
+ query,
102
+ },
103
+ }
104
+ );
105
+
106
+ if (response.error) throw response.error;
107
+
108
+ const data = response.data?.data ?? [];
109
+ spans.push(...(data as SpanData[]));
110
+ totalFetched += data.length;
111
+
112
+ cursor = response.data?.next_cursor ?? null;
113
+
114
+ // Stop if there's no more data
115
+ if (!cursor || data.length === 0) {
116
+ break;
117
+ }
118
+ }
119
+
120
+ // Write spans to JSONL file
121
+ const jsonlContent = spans.map((span) => JSON.stringify(span)).join("\n");
122
+ await mode.writeFile(
123
+ `/phoenix/projects/${projectName}/spans/index.jsonl`,
124
+ jsonlContent
125
+ );
126
+
127
+ // Write metadata about the spans snapshot
128
+ const metadata = {
129
+ project: projectName,
130
+ spanCount: spans.length,
131
+ startTime: startTime || null,
132
+ endTime: endTime || null,
133
+ snapshotTime: new Date().toISOString(),
134
+ };
135
+
136
+ await mode.writeFile(
137
+ `/phoenix/projects/${projectName}/spans/metadata.json`,
138
+ JSON.stringify(metadata, null, 2)
139
+ );
140
+ }, `fetching spans for project ${projectName}`);
141
+ }
142
+ }