@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.
- package/LICENSE +201 -0
- package/README.md +620 -0
- package/dist/agent/index.js +230 -0
- package/dist/cli.js +640 -0
- package/dist/commands/index.js +2 -0
- package/dist/commands/px-fetch-more-spans.js +98 -0
- package/dist/commands/px-fetch-more-trace.js +110 -0
- package/dist/config/index.js +165 -0
- package/dist/config/loader.js +141 -0
- package/dist/config/schema.js +53 -0
- package/dist/index.js +1 -0
- package/dist/modes/index.js +17 -0
- package/dist/modes/local.js +134 -0
- package/dist/modes/sandbox.js +121 -0
- package/dist/modes/types.js +1 -0
- package/dist/observability/index.js +65 -0
- package/dist/progress.js +209 -0
- package/dist/prompts/index.js +1 -0
- package/dist/prompts/system.js +30 -0
- package/dist/snapshot/client.js +74 -0
- package/dist/snapshot/context.js +332 -0
- package/dist/snapshot/datasets.js +68 -0
- package/dist/snapshot/experiments.js +135 -0
- package/dist/snapshot/index.js +262 -0
- package/dist/snapshot/projects.js +44 -0
- package/dist/snapshot/prompts.js +199 -0
- package/dist/snapshot/spans.js +80 -0
- package/dist/tsconfig.esm.tsbuildinfo +1 -0
- package/package.json +75 -0
- package/src/agent/index.ts +323 -0
- package/src/cli.ts +782 -0
- package/src/commands/index.ts +8 -0
- package/src/commands/px-fetch-more-spans.ts +174 -0
- package/src/commands/px-fetch-more-trace.ts +183 -0
- package/src/config/index.ts +225 -0
- package/src/config/loader.ts +173 -0
- package/src/config/schema.ts +66 -0
- package/src/index.ts +1 -0
- package/src/modes/index.ts +21 -0
- package/src/modes/local.ts +163 -0
- package/src/modes/sandbox.ts +144 -0
- package/src/modes/types.ts +31 -0
- package/src/observability/index.ts +90 -0
- package/src/progress.ts +239 -0
- package/src/prompts/index.ts +1 -0
- package/src/prompts/system.ts +31 -0
- package/src/snapshot/client.ts +129 -0
- package/src/snapshot/context.ts +462 -0
- package/src/snapshot/datasets.ts +132 -0
- package/src/snapshot/experiments.ts +246 -0
- package/src/snapshot/index.ts +403 -0
- package/src/snapshot/projects.ts +58 -0
- package/src/snapshot/prompts.ts +267 -0
- 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
|
+
}
|