@cephalization/phoenix-insight 0.4.0 → 1.0.2

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 (76) hide show
  1. package/README.md +195 -1
  2. package/dist/chunk-KEQDYZIE.js +237 -0
  3. package/dist/chunk-KEQDYZIE.js.map +1 -0
  4. package/dist/cli.d.ts +1 -0
  5. package/dist/cli.js +3950 -642
  6. package/dist/cli.js.map +1 -0
  7. package/dist/index.d.ts +108 -0
  8. package/dist/index.js +13 -1
  9. package/dist/index.js.map +1 -0
  10. package/dist/ui/assets/code-block-F6WJLWQG-BTdTzfvl.js +154 -0
  11. package/dist/ui/assets/code-block-F6WJLWQG-BTdTzfvl.js.map +1 -0
  12. package/dist/ui/assets/index-CX8aDatf.css +1 -0
  13. package/dist/ui/assets/index-DjZuAW6Y.js +63 -0
  14. package/dist/ui/assets/index-DjZuAW6Y.js.map +1 -0
  15. package/dist/ui/assets/vendor-data-r1ZEkUds.js +40 -0
  16. package/dist/ui/assets/vendor-data-r1ZEkUds.js.map +1 -0
  17. package/dist/ui/assets/vendor-react-Cgg2GOmP.js +2 -0
  18. package/dist/ui/assets/vendor-react-Cgg2GOmP.js.map +1 -0
  19. package/dist/ui/assets/vendor-render-DoMl5bum.js +381 -0
  20. package/dist/ui/assets/vendor-render-DoMl5bum.js.map +1 -0
  21. package/dist/ui/assets/vendor-ui-Cg-YC4hK.js +46 -0
  22. package/dist/ui/assets/vendor-ui-Cg-YC4hK.js.map +1 -0
  23. package/dist/ui/index.html +18 -0
  24. package/dist/ui/vite.svg +1 -0
  25. package/package.json +14 -15
  26. package/dist/agent/index.js +0 -230
  27. package/dist/commands/index.js +0 -2
  28. package/dist/commands/px-fetch-more-spans.js +0 -98
  29. package/dist/commands/px-fetch-more-trace.js +0 -110
  30. package/dist/config/index.js +0 -165
  31. package/dist/config/loader.js +0 -141
  32. package/dist/config/schema.js +0 -53
  33. package/dist/modes/index.js +0 -17
  34. package/dist/modes/local.js +0 -134
  35. package/dist/modes/sandbox.js +0 -121
  36. package/dist/modes/types.js +0 -1
  37. package/dist/observability/index.js +0 -65
  38. package/dist/progress.js +0 -209
  39. package/dist/prompts/index.js +0 -1
  40. package/dist/prompts/system.js +0 -30
  41. package/dist/snapshot/client.js +0 -74
  42. package/dist/snapshot/context.js +0 -441
  43. package/dist/snapshot/datasets.js +0 -68
  44. package/dist/snapshot/experiments.js +0 -135
  45. package/dist/snapshot/index.js +0 -262
  46. package/dist/snapshot/projects.js +0 -44
  47. package/dist/snapshot/prompts.js +0 -199
  48. package/dist/snapshot/spans.js +0 -104
  49. package/dist/snapshot/utils.js +0 -112
  50. package/dist/tsconfig.esm.tsbuildinfo +0 -1
  51. package/src/agent/index.ts +0 -323
  52. package/src/cli.ts +0 -854
  53. package/src/commands/index.ts +0 -8
  54. package/src/commands/px-fetch-more-spans.ts +0 -174
  55. package/src/commands/px-fetch-more-trace.ts +0 -183
  56. package/src/config/index.ts +0 -225
  57. package/src/config/loader.ts +0 -173
  58. package/src/config/schema.ts +0 -66
  59. package/src/index.ts +0 -1
  60. package/src/modes/index.ts +0 -21
  61. package/src/modes/local.ts +0 -163
  62. package/src/modes/sandbox.ts +0 -144
  63. package/src/modes/types.ts +0 -31
  64. package/src/observability/index.ts +0 -90
  65. package/src/progress.ts +0 -239
  66. package/src/prompts/index.ts +0 -1
  67. package/src/prompts/system.ts +0 -31
  68. package/src/snapshot/client.ts +0 -129
  69. package/src/snapshot/context.ts +0 -587
  70. package/src/snapshot/datasets.ts +0 -132
  71. package/src/snapshot/experiments.ts +0 -246
  72. package/src/snapshot/index.ts +0 -403
  73. package/src/snapshot/projects.ts +0 -58
  74. package/src/snapshot/prompts.ts +0 -267
  75. package/src/snapshot/spans.ts +0 -163
  76. package/src/snapshot/utils.ts +0 -140
@@ -1,267 +0,0 @@
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
- }
@@ -1,163 +0,0 @@
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
- /** Enable debug logging (default: uses DEBUG env var) */
13
- debug?: boolean;
14
- }
15
-
16
- /**
17
- * Debug logger that respects the debug flag or DEBUG environment variable
18
- */
19
- function createDebugLogger(debug?: boolean) {
20
- const isDebugEnabled = debug ?? !!process.env.DEBUG;
21
- return {
22
- log: (message: string) => {
23
- if (isDebugEnabled) {
24
- console.log(`[snapshotSpans] ${message}`);
25
- }
26
- },
27
- };
28
- }
29
-
30
- interface SpanData {
31
- id: string;
32
- name: string;
33
- context: {
34
- trace_id: string;
35
- span_id: string;
36
- };
37
- span_kind: string;
38
- parent_id: string | null;
39
- start_time: string;
40
- end_time: string;
41
- status_code: string;
42
- status_message: string;
43
- attributes: Record<string, unknown>;
44
- events: Array<unknown>;
45
- }
46
-
47
- interface ProjectMetadata {
48
- name: string;
49
- }
50
-
51
- /**
52
- * Fetches spans for all projects and writes them to the snapshot
53
- *
54
- * @param client - Phoenix client instance
55
- * @param mode - Execution mode for file operations
56
- * @param options - Options for filtering and limiting spans
57
- */
58
- export async function snapshotSpans(
59
- client: PhoenixClient,
60
- mode: ExecutionMode,
61
- options: SnapshotSpansOptions = {}
62
- ): Promise<void> {
63
- const { startTime, endTime, spansPerProject = 1000, debug } = options;
64
- const logger = createDebugLogger(debug);
65
-
66
- // Read projects index to get project names
67
- // Use relative path so it works with the cwd set by the execution mode
68
- logger.log("Reading projects index from projects/index.jsonl");
69
- const projectsIndexContent = await mode.exec("cat projects/index.jsonl");
70
- if (!projectsIndexContent.stdout) {
71
- // No projects, nothing to do
72
- logger.log("No projects found in index, skipping span fetch");
73
- return;
74
- }
75
-
76
- const projectNames = projectsIndexContent.stdout
77
- .trim()
78
- .split("\n")
79
- .filter((line) => line.length > 0)
80
- .map((line) => {
81
- const project = JSON.parse(line) as ProjectMetadata;
82
- return project.name;
83
- });
84
-
85
- logger.log(`Found ${projectNames.length} project(s): ${projectNames.join(", ")}`);
86
-
87
- // Fetch spans for each project
88
- for (const projectName of projectNames) {
89
- await withErrorHandling(async () => {
90
- logger.log(`Starting span fetch for project: ${projectName}`);
91
- const spans: SpanData[] = [];
92
- let cursor: string | null = null;
93
- let totalFetched = 0;
94
-
95
- // Paginate through spans until we reach the limit or no more data
96
- while (totalFetched < spansPerProject) {
97
- const query: Record<string, any> = {
98
- limit: Math.min(100, spansPerProject - totalFetched), // Fetch in chunks of 100
99
- };
100
-
101
- if (cursor) {
102
- query.cursor = cursor;
103
- }
104
-
105
- if (startTime) {
106
- query.start_time =
107
- startTime instanceof Date ? startTime.toISOString() : startTime;
108
- }
109
-
110
- if (endTime) {
111
- query.end_time =
112
- endTime instanceof Date ? endTime.toISOString() : endTime;
113
- }
114
-
115
- const response = await client.GET(
116
- "/v1/projects/{project_identifier}/spans",
117
- {
118
- params: {
119
- path: {
120
- project_identifier: projectName,
121
- },
122
- query,
123
- },
124
- }
125
- );
126
-
127
- if (response.error) throw response.error;
128
-
129
- const data = response.data?.data ?? [];
130
- spans.push(...(data as SpanData[]));
131
- totalFetched += data.length;
132
-
133
- cursor = response.data?.next_cursor ?? null;
134
-
135
- // Stop if there's no more data
136
- if (!cursor || data.length === 0) {
137
- break;
138
- }
139
- }
140
-
141
- logger.log(`Completed span fetch for project ${projectName}: ${spans.length} span(s) fetched`);
142
-
143
- // Write spans to JSONL file
144
- const spansFilePath = `/phoenix/projects/${projectName}/spans/index.jsonl`;
145
- logger.log(`Writing spans to ${spansFilePath}`);
146
- const jsonlContent = spans.map((span) => JSON.stringify(span)).join("\n");
147
- await mode.writeFile(spansFilePath, jsonlContent);
148
-
149
- // Write metadata about the spans snapshot
150
- const metadataFilePath = `/phoenix/projects/${projectName}/spans/metadata.json`;
151
- logger.log(`Writing metadata to ${metadataFilePath}`);
152
- const metadata = {
153
- project: projectName,
154
- spanCount: spans.length,
155
- startTime: startTime || null,
156
- endTime: endTime || null,
157
- snapshotTime: new Date().toISOString(),
158
- };
159
-
160
- await mode.writeFile(metadataFilePath, JSON.stringify(metadata, null, 2));
161
- }, `fetching spans for project ${projectName}`);
162
- }
163
- }
@@ -1,140 +0,0 @@
1
- /**
2
- * Snapshot discovery utilities
3
- *
4
- * Functions for listing and finding snapshots in the local filesystem.
5
- */
6
-
7
- import * as fs from "node:fs/promises";
8
- import * as path from "node:path";
9
- import * as os from "node:os";
10
-
11
- /**
12
- * Information about a single snapshot
13
- */
14
- export interface SnapshotInfo {
15
- /** Absolute path to the snapshot directory (the 'phoenix' subdirectory) */
16
- path: string;
17
- /** Timestamp when the snapshot was created (from directory name) */
18
- timestamp: Date;
19
- /** Unique identifier for the snapshot (directory name) */
20
- id: string;
21
- }
22
-
23
- /**
24
- * Get the base snapshots directory path
25
- */
26
- export function getSnapshotsDir(): string {
27
- return path.join(os.homedir(), ".phoenix-insight", "snapshots");
28
- }
29
-
30
- /**
31
- * Parse a snapshot directory name to extract timestamp
32
- *
33
- * Directory names are in format: `<timestamp>-<random>` where timestamp is Date.now()
34
- * Example: "1704067200000-abc123" -> Date(2024-01-01T00:00:00.000Z)
35
- *
36
- * @param dirName - The directory name to parse
37
- * @returns The parsed timestamp as Date, or null if invalid
38
- */
39
- function parseSnapshotDirName(dirName: string): Date | null {
40
- // Format: <timestamp>-<random>
41
- const match = dirName.match(/^(\d+)-[\w]+$/);
42
- if (!match || !match[1]) {
43
- return null;
44
- }
45
-
46
- const timestamp = parseInt(match[1], 10);
47
- if (isNaN(timestamp) || timestamp <= 0) {
48
- return null;
49
- }
50
-
51
- const date = new Date(timestamp);
52
- // Validate the date is reasonable (between year 2000 and year 3000)
53
- // Use UTC year to avoid timezone issues
54
- const year = date.getUTCFullYear();
55
- if (year < 2000 || year > 3000) {
56
- return null;
57
- }
58
-
59
- return date;
60
- }
61
-
62
- /**
63
- * List all available snapshots
64
- *
65
- * Scans the snapshots directory and returns information about each valid snapshot.
66
- * Results are sorted by timestamp descending (most recent first).
67
- *
68
- * @returns Array of snapshot info objects, sorted by timestamp descending
69
- */
70
- export async function listSnapshots(): Promise<SnapshotInfo[]> {
71
- const snapshotsDir = getSnapshotsDir();
72
-
73
- // Check if snapshots directory exists
74
- try {
75
- await fs.access(snapshotsDir);
76
- } catch {
77
- // Directory doesn't exist - return empty array
78
- return [];
79
- }
80
-
81
- // Read directory contents
82
- let entries: string[];
83
- try {
84
- entries = await fs.readdir(snapshotsDir);
85
- } catch {
86
- // Cannot read directory - return empty array
87
- return [];
88
- }
89
-
90
- // Filter and parse valid snapshot directories
91
- const snapshots: SnapshotInfo[] = [];
92
-
93
- for (const entry of entries) {
94
- const timestamp = parseSnapshotDirName(entry);
95
- if (!timestamp) {
96
- // Invalid directory name format - skip
97
- continue;
98
- }
99
-
100
- const snapshotPath = path.join(snapshotsDir, entry, "phoenix");
101
-
102
- // Verify the phoenix subdirectory exists
103
- try {
104
- const stat = await fs.stat(snapshotPath);
105
- if (!stat.isDirectory()) {
106
- continue;
107
- }
108
- } catch {
109
- // Phoenix subdirectory doesn't exist or can't be accessed - skip
110
- continue;
111
- }
112
-
113
- snapshots.push({
114
- path: snapshotPath,
115
- timestamp,
116
- id: entry,
117
- });
118
- }
119
-
120
- // Sort by timestamp descending (most recent first)
121
- snapshots.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
122
-
123
- return snapshots;
124
- }
125
-
126
- /**
127
- * Get the latest (most recent) snapshot
128
- *
129
- * @returns The most recent snapshot info, or null if no snapshots exist
130
- */
131
- export async function getLatestSnapshot(): Promise<SnapshotInfo | null> {
132
- const snapshots = await listSnapshots();
133
-
134
- if (snapshots.length === 0) {
135
- return null;
136
- }
137
-
138
- // First element is the most recent due to descending sort
139
- return snapshots[0] ?? null;
140
- }