@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
package/src/progress.ts
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Progress indicators for Phoenix Insight CLI
|
|
3
|
+
*/
|
|
4
|
+
import ora, { type Ora } from "ora";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Progress indicator for snapshot operations
|
|
8
|
+
*/
|
|
9
|
+
export class SnapshotProgress {
|
|
10
|
+
private spinner: Ora | null = null;
|
|
11
|
+
private enabled: boolean;
|
|
12
|
+
private currentPhase: string | null = null;
|
|
13
|
+
private totalSteps = 6;
|
|
14
|
+
private currentStep = 0;
|
|
15
|
+
|
|
16
|
+
constructor(enabled: boolean = true) {
|
|
17
|
+
this.enabled = enabled;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Start the progress indicator
|
|
22
|
+
*/
|
|
23
|
+
start(message: string = "Creating Phoenix data snapshot") {
|
|
24
|
+
if (!this.enabled) return;
|
|
25
|
+
|
|
26
|
+
this.currentStep = 0;
|
|
27
|
+
this.spinner = ora({
|
|
28
|
+
text: message,
|
|
29
|
+
spinner: "dots",
|
|
30
|
+
color: "blue",
|
|
31
|
+
}).start();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Update progress with a new phase
|
|
36
|
+
*/
|
|
37
|
+
update(phase: string, detail?: string) {
|
|
38
|
+
if (!this.enabled || !this.spinner) return;
|
|
39
|
+
|
|
40
|
+
this.currentStep++;
|
|
41
|
+
this.currentPhase = phase;
|
|
42
|
+
|
|
43
|
+
const progress = Math.round((this.currentStep / this.totalSteps) * 100);
|
|
44
|
+
const progressBar = this.createProgressBar(progress);
|
|
45
|
+
|
|
46
|
+
const message = detail
|
|
47
|
+
? `${progressBar} ${phase}: ${detail}`
|
|
48
|
+
: `${progressBar} ${phase}`;
|
|
49
|
+
|
|
50
|
+
this.spinner.text = message;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Complete a phase successfully
|
|
55
|
+
*/
|
|
56
|
+
succeed(message?: string) {
|
|
57
|
+
if (!this.enabled || !this.spinner) return;
|
|
58
|
+
|
|
59
|
+
const finalMessage =
|
|
60
|
+
message || `✓ ${this.currentPhase || "Snapshot"} complete`;
|
|
61
|
+
this.spinner.succeed(finalMessage);
|
|
62
|
+
this.spinner = null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Fail with an error
|
|
67
|
+
*/
|
|
68
|
+
fail(message?: string) {
|
|
69
|
+
if (!this.enabled || !this.spinner) return;
|
|
70
|
+
|
|
71
|
+
const finalMessage =
|
|
72
|
+
message || `✗ ${this.currentPhase || "Snapshot"} failed`;
|
|
73
|
+
this.spinner.fail(finalMessage);
|
|
74
|
+
this.spinner = null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Stop without success/fail status
|
|
79
|
+
*/
|
|
80
|
+
stop() {
|
|
81
|
+
if (!this.enabled || !this.spinner) return;
|
|
82
|
+
|
|
83
|
+
this.spinner.stop();
|
|
84
|
+
this.spinner = null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Create a progress bar string
|
|
89
|
+
*/
|
|
90
|
+
private createProgressBar(percentage: number): string {
|
|
91
|
+
const width = 20;
|
|
92
|
+
const filled = Math.round((percentage / 100) * width);
|
|
93
|
+
const empty = width - filled;
|
|
94
|
+
|
|
95
|
+
const bar = "█".repeat(filled) + "░".repeat(empty);
|
|
96
|
+
return `[${bar}] ${percentage}%`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Progress indicator for agent thinking
|
|
102
|
+
*/
|
|
103
|
+
export class AgentProgress {
|
|
104
|
+
private spinner: Ora | null = null;
|
|
105
|
+
private enabled: boolean;
|
|
106
|
+
private stepCount = 0;
|
|
107
|
+
private currentTool: string | null = null;
|
|
108
|
+
|
|
109
|
+
constructor(enabled: boolean = true) {
|
|
110
|
+
this.enabled = enabled;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Start thinking indicator
|
|
115
|
+
*/
|
|
116
|
+
startThinking() {
|
|
117
|
+
if (!this.enabled) return;
|
|
118
|
+
|
|
119
|
+
this.stepCount = 0;
|
|
120
|
+
this.currentTool = null;
|
|
121
|
+
this.spinner = ora({
|
|
122
|
+
text: "🤔 Analyzing...",
|
|
123
|
+
spinner: "dots",
|
|
124
|
+
color: "cyan",
|
|
125
|
+
}).start();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Update with current tool usage
|
|
130
|
+
*/
|
|
131
|
+
updateTool(toolName: string, detail?: string) {
|
|
132
|
+
if (!this.enabled || !this.spinner) return;
|
|
133
|
+
|
|
134
|
+
this.stepCount++;
|
|
135
|
+
this.currentTool = toolName;
|
|
136
|
+
|
|
137
|
+
// Map tool names to more user-friendly descriptions
|
|
138
|
+
const friendlyNames: Record<string, string> = {
|
|
139
|
+
bash: "Running command",
|
|
140
|
+
px_fetch_more_spans: "Fetching additional spans",
|
|
141
|
+
px_fetch_more_trace: "Fetching trace details",
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const displayName = friendlyNames[toolName] || toolName;
|
|
145
|
+
const message = detail
|
|
146
|
+
? `🔧 ${displayName}: ${detail}`
|
|
147
|
+
: `🔧 ${displayName} (step ${this.stepCount})`;
|
|
148
|
+
|
|
149
|
+
this.spinner.text = message;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Update with tool result
|
|
154
|
+
*/
|
|
155
|
+
updateToolResult(toolName: string, success: boolean = true) {
|
|
156
|
+
if (!this.enabled || !this.spinner) return;
|
|
157
|
+
|
|
158
|
+
const icon = success ? "✓" : "✗";
|
|
159
|
+
const status = success ? "completed" : "failed";
|
|
160
|
+
|
|
161
|
+
// More informative messages for each tool
|
|
162
|
+
const toolMessages: Record<string, string> = {
|
|
163
|
+
bash: "Command executed",
|
|
164
|
+
px_fetch_more_spans: "Additional spans fetched",
|
|
165
|
+
px_fetch_more_trace: "Trace details fetched",
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const baseMessage = toolMessages[toolName] || `Tool ${toolName}`;
|
|
169
|
+
const message = `${icon} ${baseMessage} ${status}`;
|
|
170
|
+
|
|
171
|
+
// Brief flash of the result before moving on
|
|
172
|
+
this.spinner.text = message;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Show progress for a specific action
|
|
177
|
+
*/
|
|
178
|
+
updateAction(action: string) {
|
|
179
|
+
if (!this.enabled || !this.spinner) return;
|
|
180
|
+
|
|
181
|
+
this.spinner.text = `🔍 ${action}...`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Stop the thinking indicator
|
|
186
|
+
*/
|
|
187
|
+
stop() {
|
|
188
|
+
if (!this.enabled || !this.spinner) return;
|
|
189
|
+
|
|
190
|
+
this.spinner.stop();
|
|
191
|
+
this.spinner = null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Complete with a success message
|
|
196
|
+
*/
|
|
197
|
+
succeed(message: string = "✨ Analysis complete") {
|
|
198
|
+
if (!this.enabled || !this.spinner) return;
|
|
199
|
+
|
|
200
|
+
this.spinner.succeed(message);
|
|
201
|
+
this.spinner = null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Simple progress logger for when spinners aren't appropriate
|
|
207
|
+
*/
|
|
208
|
+
export class SimpleProgress {
|
|
209
|
+
private enabled: boolean;
|
|
210
|
+
|
|
211
|
+
constructor(enabled: boolean = true) {
|
|
212
|
+
this.enabled = enabled;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
log(message: string) {
|
|
216
|
+
if (!this.enabled) return;
|
|
217
|
+
console.log(`[Phoenix Insight] ${message}`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
info(message: string) {
|
|
221
|
+
if (!this.enabled) return;
|
|
222
|
+
console.log(`ℹ️ ${message}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
success(message: string) {
|
|
226
|
+
if (!this.enabled) return;
|
|
227
|
+
console.log(`✅ ${message}`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
warning(message: string) {
|
|
231
|
+
if (!this.enabled) return;
|
|
232
|
+
console.log(`⚠️ ${message}`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
error(message: string) {
|
|
236
|
+
if (!this.enabled) return;
|
|
237
|
+
console.log(`❌ ${message}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { INSIGHT_SYSTEM_PROMPT } from "./system.js";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System prompt for the Phoenix Insight AI agent
|
|
3
|
+
* This prompt teaches the agent about the filesystem layout and available commands
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const INSIGHT_SYSTEM_PROMPT = `You are an expert at analyzing Phoenix observability data.
|
|
7
|
+
|
|
8
|
+
**START by reading /phoenix/_context.md** - it contains a summary of what's available.
|
|
9
|
+
|
|
10
|
+
You have access to a bash shell with Phoenix data organized as files:
|
|
11
|
+
|
|
12
|
+
/phoenix/
|
|
13
|
+
_context.md - READ THIS FIRST: summary of available data
|
|
14
|
+
/projects/{name}/spans/ - Span data (JSONL format, may be sampled)
|
|
15
|
+
/datasets/ - Datasets and examples
|
|
16
|
+
/experiments/ - Experiment runs and results
|
|
17
|
+
/prompts/ - Prompt templates and versions
|
|
18
|
+
|
|
19
|
+
Use commands like:
|
|
20
|
+
- cat, head, tail: Read file contents
|
|
21
|
+
- grep: Search for patterns
|
|
22
|
+
- jq: Query and transform JSON/JSONL
|
|
23
|
+
- ls, find: Navigate and discover data
|
|
24
|
+
- sort, uniq, wc: Aggregate and count
|
|
25
|
+
- awk: Complex text processing
|
|
26
|
+
|
|
27
|
+
If you need MORE data than what's in the snapshot:
|
|
28
|
+
- px-fetch-more spans --project <name> --limit 500
|
|
29
|
+
- px-fetch-more trace --trace-id <id>
|
|
30
|
+
|
|
31
|
+
This is a READ-ONLY snapshot. Start with _context.md, then explore to answer the question.`;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { createClient, type PhoenixClient } from "@arizeai/phoenix-client";
|
|
2
|
+
|
|
3
|
+
export class PhoenixClientError extends Error {
|
|
4
|
+
public code:
|
|
5
|
+
| "NETWORK_ERROR"
|
|
6
|
+
| "AUTH_ERROR"
|
|
7
|
+
| "INVALID_RESPONSE"
|
|
8
|
+
| "UNKNOWN_ERROR";
|
|
9
|
+
public originalError?: unknown;
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
message: string,
|
|
13
|
+
code: "NETWORK_ERROR" | "AUTH_ERROR" | "INVALID_RESPONSE" | "UNKNOWN_ERROR",
|
|
14
|
+
originalError?: unknown
|
|
15
|
+
) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "PhoenixClientError";
|
|
18
|
+
this.code = code;
|
|
19
|
+
this.originalError = originalError;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface PhoenixClientConfig {
|
|
24
|
+
baseURL?: string;
|
|
25
|
+
apiKey?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Creates a wrapped Phoenix client with error handling
|
|
30
|
+
*/
|
|
31
|
+
export function createPhoenixClient(
|
|
32
|
+
config: PhoenixClientConfig = {}
|
|
33
|
+
): PhoenixClient {
|
|
34
|
+
const headers: Record<string, string> = {};
|
|
35
|
+
|
|
36
|
+
if (config.apiKey) {
|
|
37
|
+
headers["api_key"] = config.apiKey;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const clientOptions: Parameters<typeof createClient>[0] = {
|
|
41
|
+
options: {
|
|
42
|
+
baseUrl: config.baseURL,
|
|
43
|
+
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return createClient(clientOptions);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Wraps an async operation with standardized error handling
|
|
52
|
+
*/
|
|
53
|
+
export async function withErrorHandling<T>(
|
|
54
|
+
operation: () => Promise<T>,
|
|
55
|
+
context: string
|
|
56
|
+
): Promise<T> {
|
|
57
|
+
try {
|
|
58
|
+
return await operation();
|
|
59
|
+
} catch (error) {
|
|
60
|
+
// Network errors
|
|
61
|
+
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
62
|
+
throw new PhoenixClientError(
|
|
63
|
+
`Network error during ${context}: Unable to connect to Phoenix server`,
|
|
64
|
+
"NETWORK_ERROR",
|
|
65
|
+
error
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// HTTP errors from the middleware
|
|
70
|
+
if (error instanceof Error && error.message.includes(": ")) {
|
|
71
|
+
const parts = error.message.split(": ", 2);
|
|
72
|
+
if (parts.length === 2 && parts[1]) {
|
|
73
|
+
const [url, statusInfo] = parts;
|
|
74
|
+
const statusParts = statusInfo.split(" ");
|
|
75
|
+
const statusCode = statusParts[0];
|
|
76
|
+
const statusText = statusParts.slice(1).join(" ");
|
|
77
|
+
|
|
78
|
+
if (statusCode === "401" || statusCode === "403") {
|
|
79
|
+
throw new PhoenixClientError(
|
|
80
|
+
`Authentication error during ${context}: ${statusText}`,
|
|
81
|
+
"AUTH_ERROR",
|
|
82
|
+
error
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (statusCode && statusCode.startsWith("4")) {
|
|
87
|
+
throw new PhoenixClientError(
|
|
88
|
+
`Client error during ${context}: ${statusCode} ${statusText}`,
|
|
89
|
+
"INVALID_RESPONSE",
|
|
90
|
+
error
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (statusCode && statusCode.startsWith("5")) {
|
|
95
|
+
throw new PhoenixClientError(
|
|
96
|
+
`Server error during ${context}: ${statusCode} ${statusText}`,
|
|
97
|
+
"NETWORK_ERROR",
|
|
98
|
+
error
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Unknown errors
|
|
105
|
+
throw new PhoenixClientError(
|
|
106
|
+
`Unexpected error during ${context}: ${error instanceof Error ? error.message : String(error)}`,
|
|
107
|
+
"UNKNOWN_ERROR",
|
|
108
|
+
error
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Helper to safely extract data from API responses
|
|
115
|
+
*/
|
|
116
|
+
export function extractData<T>(response: { data?: T; error?: unknown }): T {
|
|
117
|
+
if (response.error) {
|
|
118
|
+
throw response.error;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!response.data) {
|
|
122
|
+
throw new PhoenixClientError(
|
|
123
|
+
"Invalid API response: missing data",
|
|
124
|
+
"INVALID_RESPONSE"
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return response.data;
|
|
129
|
+
}
|