@prefactor/core 0.1.1 → 0.2.1
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/README.md +35 -2
- package/dist/agent/instance-manager.d.ts +16 -0
- package/dist/agent/instance-manager.d.ts.map +1 -0
- package/dist/agent/instance-manager.js +50 -0
- package/dist/agent/instance-manager.js.map +1 -0
- package/dist/config.d.ts +28 -52
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +33 -18
- package/dist/config.js.map +1 -1
- package/dist/create-core.d.ts +10 -0
- package/dist/create-core.d.ts.map +1 -0
- package/dist/create-core.js +31 -0
- package/dist/create-core.js.map +1 -0
- package/dist/index.cjs +632 -256
- package/dist/index.cjs.map +18 -11
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +633 -257
- package/dist/index.js.map +18 -11
- package/dist/queue/actions.d.ts +24 -0
- package/dist/queue/actions.d.ts.map +1 -0
- package/dist/queue/actions.js +2 -0
- package/dist/queue/actions.js.map +1 -0
- package/dist/queue/base.d.ts +19 -0
- package/dist/queue/base.d.ts.map +1 -0
- package/dist/{transport → queue}/base.js.map +1 -1
- package/dist/queue/in-memory-queue.d.ts +11 -0
- package/dist/queue/in-memory-queue.d.ts.map +1 -0
- package/dist/queue/in-memory-queue.js +46 -0
- package/dist/queue/in-memory-queue.js.map +1 -0
- package/dist/queue/task-executor.d.ts +18 -0
- package/dist/queue/task-executor.d.ts.map +1 -0
- package/dist/queue/task-executor.js +77 -0
- package/dist/queue/task-executor.js.map +1 -0
- package/dist/tracing/context.d.ts +12 -0
- package/dist/tracing/context.d.ts.map +1 -1
- package/dist/tracing/context.js +41 -5
- package/dist/tracing/context.js.map +1 -1
- package/dist/tracing/span.d.ts +7 -9
- package/dist/tracing/span.d.ts.map +1 -1
- package/dist/tracing/span.js +6 -8
- package/dist/tracing/span.js.map +1 -1
- package/dist/tracing/tracer.d.ts +5 -16
- package/dist/tracing/tracer.d.ts.map +1 -1
- package/dist/tracing/tracer.js +22 -26
- package/dist/tracing/tracer.js.map +1 -1
- package/dist/transport/http/agent-instance-client.d.ts +23 -0
- package/dist/transport/http/agent-instance-client.d.ts.map +1 -0
- package/dist/transport/http/agent-instance-client.js +25 -0
- package/dist/transport/http/agent-instance-client.js.map +1 -0
- package/dist/transport/http/agent-span-client.d.ts +25 -0
- package/dist/transport/http/agent-span-client.d.ts.map +1 -0
- package/dist/transport/http/agent-span-client.js +37 -0
- package/dist/transport/http/agent-span-client.js.map +1 -0
- package/dist/transport/http/http-client.d.ts +43 -0
- package/dist/transport/http/http-client.d.ts.map +1 -0
- package/dist/transport/http/http-client.js +127 -0
- package/dist/transport/http/http-client.js.map +1 -0
- package/dist/transport/http/retry-policy.d.ts +4 -0
- package/dist/transport/http/retry-policy.d.ts.map +1 -0
- package/dist/transport/http/retry-policy.js +10 -0
- package/dist/transport/http/retry-policy.js.map +1 -0
- package/dist/transport/http.d.ts +30 -72
- package/dist/transport/http.d.ts.map +1 -1
- package/dist/transport/http.js +146 -269
- package/dist/transport/http.js.map +1 -1
- package/dist/utils/logging.d.ts.map +1 -1
- package/dist/utils/logging.js +7 -1
- package/dist/utils/logging.js.map +1 -1
- package/package.json +1 -1
- package/dist/transport/base.d.ts +0 -38
- package/dist/transport/base.d.ts.map +0 -1
- package/dist/transport/stdio.d.ts +0 -48
- package/dist/transport/stdio.d.ts.map +0 -1
- package/dist/transport/stdio.js +0 -71
- package/dist/transport/stdio.js.map +0 -1
- /package/dist/{transport → queue}/base.js +0 -0
package/dist/index.js
CHANGED
|
@@ -1,41 +1,92 @@
|
|
|
1
|
+
// packages/core/src/agent/instance-manager.ts
|
|
2
|
+
class AgentInstanceManager {
|
|
3
|
+
transport;
|
|
4
|
+
options;
|
|
5
|
+
registeredSchema = null;
|
|
6
|
+
constructor(transport, options) {
|
|
7
|
+
this.transport = transport;
|
|
8
|
+
this.options = options;
|
|
9
|
+
}
|
|
10
|
+
registerSchema(schema) {
|
|
11
|
+
if (this.registeredSchema === null) {
|
|
12
|
+
this.registeredSchema = schema;
|
|
13
|
+
this.transport.registerSchema(schema);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const existingSchema = stableStringify(this.registeredSchema);
|
|
17
|
+
const incomingSchema = stableStringify(schema);
|
|
18
|
+
if (existingSchema !== incomingSchema) {
|
|
19
|
+
console.warn("A different schema was provided after registration; ignoring subsequent schema.");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
startInstance(options = {}) {
|
|
23
|
+
if (!this.options.allowUnregisteredSchema && this.registeredSchema === null) {
|
|
24
|
+
console.warn("Schema must be registered before starting an agent instance.");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
this.transport.startAgentInstance(options);
|
|
28
|
+
}
|
|
29
|
+
finishInstance() {
|
|
30
|
+
this.transport.finishAgentInstance();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function stableStringify(value) {
|
|
34
|
+
return JSON.stringify(normalizeValue(value));
|
|
35
|
+
}
|
|
36
|
+
function normalizeValue(value) {
|
|
37
|
+
if (Array.isArray(value)) {
|
|
38
|
+
return value.map((entry) => normalizeValue(entry));
|
|
39
|
+
}
|
|
40
|
+
if (value && typeof value === "object") {
|
|
41
|
+
const normalized = {};
|
|
42
|
+
const objectValue = value;
|
|
43
|
+
const keys = Object.keys(objectValue).sort((a, b) => a.localeCompare(b));
|
|
44
|
+
for (const key of keys) {
|
|
45
|
+
normalized[key] = normalizeValue(objectValue[key]);
|
|
46
|
+
}
|
|
47
|
+
return normalized;
|
|
48
|
+
}
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
1
51
|
// packages/core/src/config.ts
|
|
2
52
|
import { z } from "zod";
|
|
53
|
+
var DEFAULT_RETRY_ON_STATUS_CODES = [
|
|
54
|
+
429,
|
|
55
|
+
...Array.from({ length: 100 }, (_, index) => 500 + index)
|
|
56
|
+
];
|
|
57
|
+
var HttpStatusCodeSchema = z.number().int().min(100).max(599);
|
|
3
58
|
var HttpTransportConfigSchema = z.object({
|
|
4
59
|
apiUrl: z.string().url(),
|
|
5
60
|
apiToken: z.string().min(1),
|
|
6
61
|
agentId: z.string().optional(),
|
|
7
|
-
|
|
62
|
+
agentIdentifier: z.string().default("v1.0.0"),
|
|
8
63
|
agentName: z.string().optional(),
|
|
9
64
|
agentDescription: z.string().optional(),
|
|
10
65
|
agentSchema: z.record(z.unknown()).optional(),
|
|
11
|
-
agentSchemaVersion: z.string().optional(),
|
|
12
|
-
skipSchema: z.boolean().default(false),
|
|
13
66
|
requestTimeout: z.number().positive().default(30000),
|
|
14
|
-
connectTimeout: z.number().positive().default(1e4),
|
|
15
67
|
maxRetries: z.number().int().nonnegative().default(3),
|
|
16
68
|
initialRetryDelay: z.number().positive().default(1000),
|
|
17
69
|
maxRetryDelay: z.number().positive().default(60000),
|
|
18
|
-
retryMultiplier: z.number().positive().default(2)
|
|
70
|
+
retryMultiplier: z.number().positive().default(2),
|
|
71
|
+
retryOnStatusCodes: z.array(HttpStatusCodeSchema).default([...DEFAULT_RETRY_ON_STATUS_CODES])
|
|
19
72
|
});
|
|
20
73
|
var PartialHttpConfigSchema = z.object({
|
|
21
74
|
apiUrl: z.string().url(),
|
|
22
75
|
apiToken: z.string().min(1),
|
|
23
76
|
agentId: z.string().optional(),
|
|
24
|
-
|
|
77
|
+
agentIdentifier: z.string().optional(),
|
|
25
78
|
agentName: z.string().optional(),
|
|
26
79
|
agentDescription: z.string().optional(),
|
|
27
80
|
agentSchema: z.record(z.unknown()).optional(),
|
|
28
|
-
agentSchemaVersion: z.string().optional(),
|
|
29
|
-
skipSchema: z.boolean().optional(),
|
|
30
81
|
requestTimeout: z.number().positive().optional(),
|
|
31
|
-
connectTimeout: z.number().positive().optional(),
|
|
32
82
|
maxRetries: z.number().int().nonnegative().optional(),
|
|
33
83
|
initialRetryDelay: z.number().positive().optional(),
|
|
34
84
|
maxRetryDelay: z.number().positive().optional(),
|
|
35
|
-
retryMultiplier: z.number().positive().optional()
|
|
85
|
+
retryMultiplier: z.number().positive().optional(),
|
|
86
|
+
retryOnStatusCodes: z.array(HttpStatusCodeSchema).optional()
|
|
36
87
|
});
|
|
37
88
|
var ConfigSchema = z.object({
|
|
38
|
-
transportType: z.enum(["
|
|
89
|
+
transportType: z.enum(["http"]).default("http"),
|
|
39
90
|
sampleRate: z.number().min(0).max(1).default(1),
|
|
40
91
|
captureInputs: z.boolean().default(true),
|
|
41
92
|
captureOutputs: z.boolean().default(true),
|
|
@@ -44,52 +95,93 @@ var ConfigSchema = z.object({
|
|
|
44
95
|
httpConfig: PartialHttpConfigSchema.optional()
|
|
45
96
|
});
|
|
46
97
|
function createConfig(options) {
|
|
98
|
+
const retryOnStatusCodesFromEnv = parseRetryOnStatusCodesEnv(process.env.PREFACTOR_RETRY_ON_STATUS_CODES);
|
|
47
99
|
const config = {
|
|
48
|
-
transportType: options?.transportType ?? process.env.PREFACTOR_TRANSPORT ?? "
|
|
100
|
+
transportType: options?.transportType ?? process.env.PREFACTOR_TRANSPORT ?? "http",
|
|
49
101
|
sampleRate: options?.sampleRate ?? parseFloat(process.env.PREFACTOR_SAMPLE_RATE ?? "1.0"),
|
|
50
102
|
captureInputs: options?.captureInputs ?? process.env.PREFACTOR_CAPTURE_INPUTS !== "false",
|
|
51
103
|
captureOutputs: options?.captureOutputs ?? process.env.PREFACTOR_CAPTURE_OUTPUTS !== "false",
|
|
52
104
|
maxInputLength: options?.maxInputLength ?? parseInt(process.env.PREFACTOR_MAX_INPUT_LENGTH ?? "10000", 10),
|
|
53
105
|
maxOutputLength: options?.maxOutputLength ?? parseInt(process.env.PREFACTOR_MAX_OUTPUT_LENGTH ?? "10000", 10),
|
|
54
|
-
httpConfig: options?.httpConfig
|
|
106
|
+
httpConfig: options?.httpConfig ? {
|
|
107
|
+
...options.httpConfig,
|
|
108
|
+
retryOnStatusCodes: options.httpConfig.retryOnStatusCodes ?? retryOnStatusCodesFromEnv
|
|
109
|
+
} : undefined
|
|
55
110
|
};
|
|
56
111
|
return ConfigSchema.parse(config);
|
|
57
112
|
}
|
|
113
|
+
function parseRetryOnStatusCodesEnv(value) {
|
|
114
|
+
if (!value) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const parsedCodes = value.split(",").map((status) => status.trim()).filter((status) => /^\d{3}$/.test(status)).map((status) => Number(status)).filter((status) => status >= 100 && status <= 599);
|
|
118
|
+
return parsedCodes.length > 0 ? parsedCodes : undefined;
|
|
119
|
+
}
|
|
120
|
+
// packages/core/src/create-core.ts
|
|
121
|
+
import { extractPartition } from "@prefactor/pfid";
|
|
122
|
+
|
|
123
|
+
// packages/core/src/tracing/tracer.ts
|
|
124
|
+
import { generate, generatePartition } from "@prefactor/pfid";
|
|
125
|
+
|
|
58
126
|
// packages/core/src/tracing/context.ts
|
|
59
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
127
|
+
import { AsyncLocalStorage, AsyncResource } from "node:async_hooks";
|
|
60
128
|
var spanStorage = new AsyncLocalStorage;
|
|
61
129
|
|
|
62
130
|
class SpanContext {
|
|
63
131
|
static getCurrent() {
|
|
64
|
-
|
|
132
|
+
const stack = spanStorage.getStore() ?? [];
|
|
133
|
+
return stack[stack.length - 1];
|
|
134
|
+
}
|
|
135
|
+
static getStack() {
|
|
136
|
+
return [...spanStorage.getStore() ?? []];
|
|
137
|
+
}
|
|
138
|
+
static enter(span) {
|
|
139
|
+
const stack = [...spanStorage.getStore() ?? [], span];
|
|
140
|
+
spanStorage.enterWith(stack);
|
|
141
|
+
}
|
|
142
|
+
static exit() {
|
|
143
|
+
const stack = [...spanStorage.getStore() ?? []];
|
|
144
|
+
stack.pop();
|
|
145
|
+
spanStorage.enterWith(stack);
|
|
65
146
|
}
|
|
66
147
|
static run(span, fn) {
|
|
67
|
-
|
|
148
|
+
const stack = spanStorage.getStore() ?? [];
|
|
149
|
+
const resource = new AsyncResource("SpanContext.run");
|
|
150
|
+
try {
|
|
151
|
+
return resource.runInAsyncScope(() => spanStorage.run([...stack, span], fn));
|
|
152
|
+
} finally {
|
|
153
|
+
resource.emitDestroy();
|
|
154
|
+
}
|
|
68
155
|
}
|
|
69
156
|
static async runAsync(span, fn) {
|
|
70
|
-
|
|
157
|
+
const stack = spanStorage.getStore() ?? [];
|
|
158
|
+
const resource = new AsyncResource("SpanContext.runAsync");
|
|
159
|
+
try {
|
|
160
|
+
return await resource.runInAsyncScope(() => spanStorage.run([...stack, span], fn));
|
|
161
|
+
} finally {
|
|
162
|
+
resource.emitDestroy();
|
|
163
|
+
}
|
|
71
164
|
}
|
|
72
165
|
static clear() {
|
|
73
166
|
spanStorage.disable();
|
|
74
167
|
}
|
|
75
168
|
}
|
|
169
|
+
|
|
76
170
|
// packages/core/src/tracing/span.ts
|
|
77
|
-
var SpanType
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
SpanType2["RETRIEVER"] = "retriever";
|
|
84
|
-
})(SpanType ||= {});
|
|
171
|
+
var SpanType = {
|
|
172
|
+
AGENT: "agent",
|
|
173
|
+
LLM: "llm",
|
|
174
|
+
TOOL: "tool",
|
|
175
|
+
CHAIN: "chain"
|
|
176
|
+
};
|
|
85
177
|
var SpanStatus;
|
|
86
178
|
((SpanStatus2) => {
|
|
87
179
|
SpanStatus2["RUNNING"] = "running";
|
|
88
180
|
SpanStatus2["SUCCESS"] = "success";
|
|
89
181
|
SpanStatus2["ERROR"] = "error";
|
|
90
182
|
})(SpanStatus ||= {});
|
|
183
|
+
|
|
91
184
|
// packages/core/src/tracing/tracer.ts
|
|
92
|
-
import { generate, generatePartition } from "@prefactor/pfid";
|
|
93
185
|
class Tracer {
|
|
94
186
|
transport;
|
|
95
187
|
partition;
|
|
@@ -98,11 +190,12 @@ class Tracer {
|
|
|
98
190
|
this.partition = partition ?? generatePartition();
|
|
99
191
|
}
|
|
100
192
|
startSpan(options) {
|
|
193
|
+
const parentSpan = SpanContext.getCurrent();
|
|
101
194
|
const spanId = generate(this.partition);
|
|
102
|
-
const traceId =
|
|
195
|
+
const traceId = parentSpan?.traceId ?? generate(this.partition);
|
|
103
196
|
const span = {
|
|
104
197
|
spanId,
|
|
105
|
-
parentSpanId:
|
|
198
|
+
parentSpanId: parentSpan?.spanId ?? null,
|
|
106
199
|
traceId,
|
|
107
200
|
name: options.name,
|
|
108
201
|
spanType: options.spanType,
|
|
@@ -113,10 +206,9 @@ class Tracer {
|
|
|
113
206
|
outputs: null,
|
|
114
207
|
tokenUsage: null,
|
|
115
208
|
error: null,
|
|
116
|
-
metadata: options.metadata ?? {}
|
|
117
|
-
tags: options.tags ?? []
|
|
209
|
+
metadata: options.metadata ?? {}
|
|
118
210
|
};
|
|
119
|
-
if (options.spanType ===
|
|
211
|
+
if (options.spanType === SpanType.AGENT) {
|
|
120
212
|
try {
|
|
121
213
|
this.transport.emit(span);
|
|
122
214
|
} catch (error) {
|
|
@@ -126,7 +218,8 @@ class Tracer {
|
|
|
126
218
|
return span;
|
|
127
219
|
}
|
|
128
220
|
endSpan(span, options) {
|
|
129
|
-
|
|
221
|
+
const endTime = Date.now();
|
|
222
|
+
span.endTime = endTime;
|
|
130
223
|
span.outputs = options?.outputs ?? null;
|
|
131
224
|
span.tokenUsage = options?.tokenUsage ?? null;
|
|
132
225
|
if (options?.error) {
|
|
@@ -140,13 +233,20 @@ class Tracer {
|
|
|
140
233
|
span.status = "success" /* SUCCESS */;
|
|
141
234
|
}
|
|
142
235
|
try {
|
|
143
|
-
if (span.spanType ===
|
|
144
|
-
this.transport.finishSpan(span.spanId,
|
|
236
|
+
if (span.spanType === SpanType.AGENT) {
|
|
237
|
+
this.transport.finishSpan(span.spanId, endTime);
|
|
145
238
|
} else {
|
|
146
239
|
this.transport.emit(span);
|
|
147
240
|
}
|
|
148
241
|
} catch (error) {
|
|
149
|
-
console.error("Failed to emit
|
|
242
|
+
console.error("Failed to emit span action:", error);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
async close() {
|
|
246
|
+
try {
|
|
247
|
+
await this.transport.close();
|
|
248
|
+
} catch (error) {
|
|
249
|
+
console.error("Failed to close transport:", error);
|
|
150
250
|
}
|
|
151
251
|
}
|
|
152
252
|
startAgentInstance() {
|
|
@@ -163,18 +263,150 @@ class Tracer {
|
|
|
163
263
|
console.error("Failed to finish agent instance:", error);
|
|
164
264
|
}
|
|
165
265
|
}
|
|
166
|
-
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// packages/core/src/queue/in-memory-queue.ts
|
|
269
|
+
class InMemoryQueue {
|
|
270
|
+
items = [];
|
|
271
|
+
waiters = [];
|
|
272
|
+
isClosed = false;
|
|
273
|
+
async put(item) {
|
|
274
|
+
if (this.isClosed) {
|
|
275
|
+
throw new Error("Cannot put item into a closed queue");
|
|
276
|
+
}
|
|
277
|
+
const waiter = this.waiters.shift();
|
|
278
|
+
if (waiter) {
|
|
279
|
+
waiter({ done: false, item });
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
this.items.push({ item });
|
|
283
|
+
}
|
|
284
|
+
async get() {
|
|
285
|
+
if (this.items.length > 0) {
|
|
286
|
+
const entry = this.items.shift();
|
|
287
|
+
if (entry) {
|
|
288
|
+
return { done: false, item: entry.item };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (this.isClosed) {
|
|
292
|
+
return { done: true };
|
|
293
|
+
}
|
|
294
|
+
return new Promise((resolve) => {
|
|
295
|
+
this.waiters.push(resolve);
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
close() {
|
|
299
|
+
if (this.isClosed) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
this.isClosed = true;
|
|
303
|
+
while (this.waiters.length > 0) {
|
|
304
|
+
const waiter = this.waiters.shift();
|
|
305
|
+
if (waiter) {
|
|
306
|
+
waiter({ done: true });
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
size() {
|
|
311
|
+
return this.items.length;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// packages/core/src/queue/task-executor.ts
|
|
316
|
+
var DEFAULT_WORKER_COUNT = 1;
|
|
317
|
+
var DEFAULT_MAX_RETRIES = 0;
|
|
318
|
+
var DEFAULT_RETRY_DELAY_MS = 0;
|
|
319
|
+
|
|
320
|
+
class TaskExecutor {
|
|
321
|
+
queue;
|
|
322
|
+
handler;
|
|
323
|
+
isRunning = false;
|
|
324
|
+
workerPromises = [];
|
|
325
|
+
workerCount;
|
|
326
|
+
maxRetries;
|
|
327
|
+
retryDelayMs;
|
|
328
|
+
onError;
|
|
329
|
+
constructor(queue, handler, options = {}) {
|
|
330
|
+
this.queue = queue;
|
|
331
|
+
this.handler = handler;
|
|
332
|
+
this.workerCount = Math.max(options.workerCount ?? DEFAULT_WORKER_COUNT, 1);
|
|
333
|
+
this.maxRetries = Math.max(options.maxRetries ?? DEFAULT_MAX_RETRIES, 0);
|
|
334
|
+
this.retryDelayMs = Math.max(options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS, 0);
|
|
335
|
+
this.onError = options.onError;
|
|
336
|
+
}
|
|
337
|
+
start() {
|
|
338
|
+
if (this.isRunning) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
this.isRunning = true;
|
|
342
|
+
this.workerPromises = Array.from({ length: this.workerCount }, () => this.runWorker());
|
|
343
|
+
}
|
|
344
|
+
async stop() {
|
|
345
|
+
if (!this.isRunning) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
this.isRunning = false;
|
|
349
|
+
this.queue.close();
|
|
350
|
+
await Promise.all(this.workerPromises);
|
|
351
|
+
this.workerPromises = [];
|
|
352
|
+
}
|
|
353
|
+
async runWorker() {
|
|
354
|
+
while (true) {
|
|
355
|
+
const result = await this.queue.get();
|
|
356
|
+
if (result.done) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
await this.executeWithRetry(result.item);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
async executeWithRetry(item) {
|
|
363
|
+
let attempt = 0;
|
|
364
|
+
while (attempt <= this.maxRetries) {
|
|
365
|
+
try {
|
|
366
|
+
await this.handler(item);
|
|
367
|
+
return;
|
|
368
|
+
} catch (error) {
|
|
369
|
+
if (attempt >= this.maxRetries) {
|
|
370
|
+
await this.safeOnError(error, item);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (this.retryDelayMs > 0) {
|
|
374
|
+
await new Promise((resolve) => setTimeout(resolve, this.retryDelayMs));
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
attempt += 1;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
async safeOnError(error, item) {
|
|
381
|
+
if (!this.onError) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
167
384
|
try {
|
|
168
|
-
await this.
|
|
169
|
-
} catch
|
|
170
|
-
|
|
385
|
+
await this.onError(error, item);
|
|
386
|
+
} catch {
|
|
387
|
+
return;
|
|
171
388
|
}
|
|
172
389
|
}
|
|
173
390
|
}
|
|
391
|
+
|
|
174
392
|
// packages/core/src/utils/logging.ts
|
|
393
|
+
var LogLevel;
|
|
394
|
+
((LogLevel2) => {
|
|
395
|
+
LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
|
|
396
|
+
LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
|
|
397
|
+
LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
|
|
398
|
+
LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
|
|
399
|
+
})(LogLevel ||= {});
|
|
400
|
+
|
|
175
401
|
class Logger {
|
|
176
402
|
namespace;
|
|
177
|
-
static level =
|
|
403
|
+
static level = (() => {
|
|
404
|
+
const level = process.env.PREFACTOR_LOG_LEVEL?.toUpperCase();
|
|
405
|
+
if (level && level in LogLevel) {
|
|
406
|
+
return LogLevel[level];
|
|
407
|
+
}
|
|
408
|
+
return 1 /* INFO */;
|
|
409
|
+
})();
|
|
178
410
|
constructor(namespace) {
|
|
179
411
|
this.namespace = namespace;
|
|
180
412
|
}
|
|
@@ -218,128 +450,358 @@ function configureLogging() {
|
|
|
218
450
|
}
|
|
219
451
|
}
|
|
220
452
|
|
|
453
|
+
// packages/core/src/transport/http/agent-instance-client.ts
|
|
454
|
+
class AgentInstanceClient {
|
|
455
|
+
httpClient;
|
|
456
|
+
constructor(httpClient) {
|
|
457
|
+
this.httpClient = httpClient;
|
|
458
|
+
}
|
|
459
|
+
register(payload) {
|
|
460
|
+
return this.httpClient.request("/api/v1/agent_instance/register", {
|
|
461
|
+
method: "POST",
|
|
462
|
+
body: payload
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
async start(agentInstanceId) {
|
|
466
|
+
await this.httpClient.request(`/api/v1/agent_instance/${agentInstanceId}/start`, {
|
|
467
|
+
method: "POST",
|
|
468
|
+
body: {}
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
async finish(agentInstanceId) {
|
|
472
|
+
await this.httpClient.request(`/api/v1/agent_instance/${agentInstanceId}/finish`, {
|
|
473
|
+
method: "POST",
|
|
474
|
+
body: {}
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// packages/core/src/transport/http/retry-policy.ts
|
|
480
|
+
var JITTER_MIN = 0.5;
|
|
481
|
+
function shouldRetryStatusCode(statusCode, retryOnStatusCodes) {
|
|
482
|
+
return retryOnStatusCodes.includes(statusCode);
|
|
483
|
+
}
|
|
484
|
+
function calculateRetryDelay(attempt, config, random = Math.random) {
|
|
485
|
+
const baseDelay = Math.min(config.initialRetryDelay * config.retryMultiplier ** attempt, config.maxRetryDelay);
|
|
486
|
+
const jitterMultiplier = JITTER_MIN + random() * JITTER_MIN;
|
|
487
|
+
return Math.round(baseDelay * jitterMultiplier);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// packages/core/src/transport/http/http-client.ts
|
|
491
|
+
class HttpClientError extends Error {
|
|
492
|
+
url;
|
|
493
|
+
method;
|
|
494
|
+
status;
|
|
495
|
+
statusText;
|
|
496
|
+
responseBody;
|
|
497
|
+
retryable;
|
|
498
|
+
constructor(message, options) {
|
|
499
|
+
super(message, { cause: options.cause });
|
|
500
|
+
this.name = "HttpClientError";
|
|
501
|
+
this.url = options.url;
|
|
502
|
+
this.method = options.method;
|
|
503
|
+
this.status = options.status;
|
|
504
|
+
this.statusText = options.statusText;
|
|
505
|
+
this.responseBody = options.responseBody;
|
|
506
|
+
this.retryable = options.retryable;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
class HttpClient {
|
|
511
|
+
config;
|
|
512
|
+
fetchFn;
|
|
513
|
+
sleep;
|
|
514
|
+
random;
|
|
515
|
+
constructor(config, dependencies = {}) {
|
|
516
|
+
this.config = config;
|
|
517
|
+
this.fetchFn = dependencies.fetchFn ?? fetch;
|
|
518
|
+
this.sleep = dependencies.sleep ?? ((delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs)));
|
|
519
|
+
this.random = dependencies.random ?? Math.random;
|
|
520
|
+
}
|
|
521
|
+
async request(path, options = {}) {
|
|
522
|
+
const url = new URL(path, this.config.apiUrl).toString();
|
|
523
|
+
const method = options.method ?? "GET";
|
|
524
|
+
let attempt = 0;
|
|
525
|
+
while (true) {
|
|
526
|
+
const headers = new Headers(options.headers);
|
|
527
|
+
headers.set("Authorization", `Bearer ${this.config.apiToken}`);
|
|
528
|
+
if (options.body !== undefined && !headers.has("Content-Type")) {
|
|
529
|
+
headers.set("Content-Type", "application/json");
|
|
530
|
+
}
|
|
531
|
+
const requestInit = {
|
|
532
|
+
...options,
|
|
533
|
+
method,
|
|
534
|
+
headers,
|
|
535
|
+
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
|
536
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? this.config.requestTimeout)
|
|
537
|
+
};
|
|
538
|
+
try {
|
|
539
|
+
const response = await this.fetchFn(url, requestInit);
|
|
540
|
+
if (response.ok) {
|
|
541
|
+
return await parseResponseBody(response);
|
|
542
|
+
}
|
|
543
|
+
const responseBody = await parseResponseBody(response);
|
|
544
|
+
const canRetry = attempt < this.config.maxRetries && shouldRetryStatusCode(response.status, this.config.retryOnStatusCodes);
|
|
545
|
+
if (canRetry) {
|
|
546
|
+
const delayMs = calculateRetryDelay(attempt, this.config, this.random);
|
|
547
|
+
await this.sleep(delayMs);
|
|
548
|
+
attempt += 1;
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
throw new HttpClientError(`HTTP request failed with status ${response.status}`, {
|
|
552
|
+
url,
|
|
553
|
+
method,
|
|
554
|
+
status: response.status,
|
|
555
|
+
statusText: response.statusText,
|
|
556
|
+
responseBody,
|
|
557
|
+
retryable: shouldRetryStatusCode(response.status, this.config.retryOnStatusCodes)
|
|
558
|
+
});
|
|
559
|
+
} catch (error) {
|
|
560
|
+
if (error instanceof HttpClientError) {
|
|
561
|
+
throw error;
|
|
562
|
+
}
|
|
563
|
+
const canRetry = attempt < this.config.maxRetries && isRetryableNetworkError(error);
|
|
564
|
+
if (canRetry) {
|
|
565
|
+
const delayMs = calculateRetryDelay(attempt, this.config, this.random);
|
|
566
|
+
await this.sleep(delayMs);
|
|
567
|
+
attempt += 1;
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
throw new HttpClientError("HTTP request failed due to network error", {
|
|
571
|
+
url,
|
|
572
|
+
method,
|
|
573
|
+
retryable: false,
|
|
574
|
+
cause: error
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
function isRetryableNetworkError(error) {
|
|
581
|
+
if (error instanceof TypeError) {
|
|
582
|
+
return true;
|
|
583
|
+
}
|
|
584
|
+
if (error instanceof DOMException && (error.name === "AbortError" || error.name === "TimeoutError")) {
|
|
585
|
+
return true;
|
|
586
|
+
}
|
|
587
|
+
if (error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError")) {
|
|
588
|
+
return true;
|
|
589
|
+
}
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
async function parseResponseBody(response) {
|
|
593
|
+
const bodyText = await response.text();
|
|
594
|
+
if (!bodyText) {
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
598
|
+
if (contentType.includes("application/json")) {
|
|
599
|
+
try {
|
|
600
|
+
return JSON.parse(bodyText);
|
|
601
|
+
} catch {
|
|
602
|
+
return bodyText;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
try {
|
|
606
|
+
return JSON.parse(bodyText);
|
|
607
|
+
} catch {
|
|
608
|
+
return bodyText;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// packages/core/src/transport/http/agent-span-client.ts
|
|
613
|
+
class AgentSpanClient {
|
|
614
|
+
httpClient;
|
|
615
|
+
constructor(httpClient) {
|
|
616
|
+
this.httpClient = httpClient;
|
|
617
|
+
}
|
|
618
|
+
create(payload) {
|
|
619
|
+
return this.httpClient.request("/api/v1/agent_spans", {
|
|
620
|
+
method: "POST",
|
|
621
|
+
body: payload
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
async finish(spanId, timestamp) {
|
|
625
|
+
try {
|
|
626
|
+
await this.httpClient.request(`/api/v1/agent_spans/${spanId}/finish`, {
|
|
627
|
+
method: "POST",
|
|
628
|
+
body: { timestamp }
|
|
629
|
+
});
|
|
630
|
+
} catch (error) {
|
|
631
|
+
if (error instanceof HttpClientError && error.status === 409 && isAlreadyFinishedError(error.responseBody)) {
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
throw error;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
function isAlreadyFinishedError(responseBody) {
|
|
639
|
+
if (!responseBody || typeof responseBody !== "object") {
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
const payload = responseBody;
|
|
643
|
+
return payload.code === "invalid_action";
|
|
644
|
+
}
|
|
645
|
+
|
|
221
646
|
// packages/core/src/transport/http.ts
|
|
222
647
|
var logger = getLogger("http-transport");
|
|
223
648
|
|
|
224
649
|
class HttpTransport {
|
|
225
650
|
config;
|
|
226
|
-
queue = [];
|
|
227
|
-
processing = false;
|
|
228
651
|
closed = false;
|
|
652
|
+
actionQueue = new InMemoryQueue;
|
|
653
|
+
taskExecutor;
|
|
654
|
+
agentInstanceClient;
|
|
655
|
+
agentSpanClient;
|
|
656
|
+
previousAgentSchema = null;
|
|
657
|
+
requiresNewAgentIdentifier = false;
|
|
658
|
+
previousAgentIdentifier = null;
|
|
229
659
|
agentInstanceId = null;
|
|
230
660
|
spanIdMap = new Map;
|
|
661
|
+
pendingFinishes = new Map;
|
|
231
662
|
constructor(config) {
|
|
232
663
|
this.config = config;
|
|
233
|
-
|
|
664
|
+
const httpClient = new HttpClient(config);
|
|
665
|
+
this.agentInstanceClient = new AgentInstanceClient(httpClient);
|
|
666
|
+
this.agentSpanClient = new AgentSpanClient(httpClient);
|
|
667
|
+
this.taskExecutor = new TaskExecutor(this.actionQueue, this.processAction, {
|
|
668
|
+
workerCount: 1,
|
|
669
|
+
onError: async (error) => {
|
|
670
|
+
logger.error("Error processing HTTP action:", error);
|
|
671
|
+
}
|
|
672
|
+
});
|
|
673
|
+
this.taskExecutor.start();
|
|
674
|
+
}
|
|
675
|
+
registerSchema(schema) {
|
|
676
|
+
this.enqueue({ type: "schema_register", schema });
|
|
677
|
+
}
|
|
678
|
+
startAgentInstance(options) {
|
|
679
|
+
this.enqueue({ type: "agent_start", options });
|
|
680
|
+
}
|
|
681
|
+
finishAgentInstance() {
|
|
682
|
+
this.enqueue({ type: "agent_finish" });
|
|
234
683
|
}
|
|
235
684
|
emit(span) {
|
|
236
|
-
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
this.queue.push({ type: "span", data: span });
|
|
685
|
+
this.enqueue({ type: "span_end", span });
|
|
240
686
|
}
|
|
241
687
|
finishSpan(spanId, endTime) {
|
|
242
|
-
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
const timestamp = new Date(endTime).toISOString();
|
|
246
|
-
this.queue.push({ type: "finish_span", data: { spanId, timestamp } });
|
|
688
|
+
this.enqueue({ type: "span_finish", spanId, endTime });
|
|
247
689
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
690
|
+
async close() {
|
|
691
|
+
this.closed = true;
|
|
692
|
+
await this.taskExecutor.stop();
|
|
693
|
+
if (this.pendingFinishes.size > 0) {
|
|
694
|
+
logger.warn(`Transport closed with ${this.pendingFinishes.size} pending span finish(es) that could not be processed`);
|
|
695
|
+
this.pendingFinishes.clear();
|
|
251
696
|
}
|
|
252
|
-
this.queue.push({ type: "start_agent", data: null });
|
|
253
697
|
}
|
|
254
|
-
|
|
698
|
+
enqueue(action) {
|
|
255
699
|
if (this.closed) {
|
|
256
700
|
return;
|
|
257
701
|
}
|
|
258
|
-
this.
|
|
702
|
+
this.actionQueue.put(action).catch((error) => {
|
|
703
|
+
logger.error("Failed to enqueue HTTP action:", error);
|
|
704
|
+
});
|
|
259
705
|
}
|
|
260
|
-
async
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
706
|
+
processAction = async (action) => {
|
|
707
|
+
switch (action.type) {
|
|
708
|
+
case "schema_register": {
|
|
709
|
+
const incomingSchema = JSON.stringify(action.schema);
|
|
710
|
+
if (this.previousAgentSchema !== null && this.previousAgentSchema !== incomingSchema) {
|
|
711
|
+
this.requiresNewAgentIdentifier = true;
|
|
712
|
+
this.previousAgentIdentifier = this.config.agentIdentifier;
|
|
713
|
+
this.agentInstanceId = null;
|
|
714
|
+
}
|
|
715
|
+
this.previousAgentSchema = incomingSchema;
|
|
716
|
+
this.config.agentSchema = action.schema;
|
|
717
|
+
return;
|
|
266
718
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
719
|
+
case "agent_start": {
|
|
720
|
+
if (this.requiresNewAgentIdentifier) {
|
|
721
|
+
const nextAgentIdentifier = action.options?.agentIdentifier;
|
|
722
|
+
if (nextAgentIdentifier === undefined || nextAgentIdentifier === this.previousAgentIdentifier) {
|
|
723
|
+
logger.error("Schema changed; starting an agent requires a new agentIdentifier value.");
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
this.requiresNewAgentIdentifier = false;
|
|
727
|
+
this.previousAgentIdentifier = null;
|
|
728
|
+
}
|
|
729
|
+
if (action.options?.agentId !== undefined)
|
|
730
|
+
this.config.agentId = action.options.agentId;
|
|
731
|
+
if (action.options?.agentIdentifier !== undefined) {
|
|
732
|
+
this.config.agentIdentifier = action.options.agentIdentifier;
|
|
733
|
+
}
|
|
734
|
+
if (action.options?.agentName !== undefined)
|
|
735
|
+
this.config.agentName = action.options.agentName;
|
|
736
|
+
if (action.options?.agentDescription !== undefined) {
|
|
737
|
+
this.config.agentDescription = action.options.agentDescription;
|
|
738
|
+
}
|
|
739
|
+
await this.startAgentInstanceHttp();
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
case "agent_finish":
|
|
743
|
+
await this.finishAgentInstanceHttp();
|
|
744
|
+
return;
|
|
745
|
+
case "span_end":
|
|
746
|
+
if (!this.agentInstanceId) {
|
|
272
747
|
await this.ensureAgentRegistered();
|
|
273
748
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
break;
|
|
284
|
-
case "finish_agent":
|
|
285
|
-
await this.finishAgentInstanceHttp();
|
|
286
|
-
break;
|
|
749
|
+
await this.sendSpan(action.span);
|
|
750
|
+
return;
|
|
751
|
+
case "span_finish": {
|
|
752
|
+
const backendSpanId = this.spanIdMap.get(action.spanId);
|
|
753
|
+
if (backendSpanId) {
|
|
754
|
+
const timestamp = new Date(action.endTime).toISOString();
|
|
755
|
+
await this.finishSpanHttp({ spanId: action.spanId, timestamp });
|
|
756
|
+
} else {
|
|
757
|
+
this.pendingFinishes.set(action.spanId, action.endTime);
|
|
287
758
|
}
|
|
288
|
-
|
|
289
|
-
logger.error("Error processing queue item:", error);
|
|
759
|
+
return;
|
|
290
760
|
}
|
|
291
761
|
}
|
|
292
|
-
|
|
762
|
+
};
|
|
763
|
+
async processPendingFinishes(spanId) {
|
|
764
|
+
if (!this.pendingFinishes.has(spanId)) {
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
const pendingEndTime = this.pendingFinishes.get(spanId);
|
|
768
|
+
if (pendingEndTime === undefined) {
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
try {
|
|
772
|
+
const timestamp = new Date(pendingEndTime).toISOString();
|
|
773
|
+
await this.finishSpanHttp({ spanId, timestamp });
|
|
774
|
+
this.pendingFinishes.delete(spanId);
|
|
775
|
+
} catch (error) {
|
|
776
|
+
logger.error("Error processing pending span finish:", error);
|
|
777
|
+
}
|
|
293
778
|
}
|
|
294
|
-
async sendSpan(span
|
|
295
|
-
const url = `${this.config.apiUrl}/api/v1/agent_spans`;
|
|
779
|
+
async sendSpan(span) {
|
|
296
780
|
const payload = this.transformSpanToApiFormat(span);
|
|
297
781
|
try {
|
|
298
|
-
const response = await
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
Authorization: `Bearer ${this.config.apiToken}`,
|
|
302
|
-
"Content-Type": "application/json"
|
|
303
|
-
},
|
|
304
|
-
body: JSON.stringify(payload),
|
|
305
|
-
signal: AbortSignal.timeout(this.config.requestTimeout)
|
|
306
|
-
});
|
|
307
|
-
if (response.ok) {
|
|
308
|
-
const data = await response.json();
|
|
309
|
-
const backendSpanId = data?.details?.id;
|
|
310
|
-
if (backendSpanId) {
|
|
311
|
-
this.spanIdMap.set(span.spanId, backendSpanId);
|
|
312
|
-
}
|
|
782
|
+
const response = await this.agentSpanClient.create(payload);
|
|
783
|
+
const backendSpanId = response.details?.id;
|
|
784
|
+
if (!backendSpanId) {
|
|
313
785
|
return;
|
|
314
786
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
logger.debug(`Retrying span send after ${delay}ms (attempt ${retry + 1})`);
|
|
318
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
319
|
-
return this.sendSpan(span, retry + 1);
|
|
320
|
-
}
|
|
321
|
-
logger.error(`Failed to send span: ${response.status} ${response.statusText}`);
|
|
787
|
+
this.spanIdMap.set(span.spanId, backendSpanId);
|
|
788
|
+
await this.processPendingFinishes(span.spanId);
|
|
322
789
|
} catch (error) {
|
|
323
790
|
logger.error("Error sending span:", error);
|
|
324
|
-
if (retry < this.config.maxRetries) {
|
|
325
|
-
const delay = Math.min(this.config.initialRetryDelay * this.config.retryMultiplier ** retry, this.config.maxRetryDelay);
|
|
326
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
327
|
-
return this.sendSpan(span, retry + 1);
|
|
328
|
-
}
|
|
329
791
|
}
|
|
330
792
|
}
|
|
331
793
|
transformSpanToApiFormat(span) {
|
|
332
794
|
const startedAt = new Date(span.startTime).toISOString();
|
|
333
795
|
const finishedAt = span.endTime ? new Date(span.endTime).toISOString() : null;
|
|
796
|
+
const apiStatus = this.mapStatusForApi(span.status);
|
|
334
797
|
const payload = {
|
|
335
798
|
span_id: span.spanId,
|
|
336
799
|
trace_id: span.traceId,
|
|
337
800
|
name: span.name,
|
|
338
|
-
status:
|
|
801
|
+
status: apiStatus,
|
|
339
802
|
inputs: span.inputs,
|
|
340
803
|
outputs: span.outputs,
|
|
341
804
|
metadata: span.metadata,
|
|
342
|
-
tags: span.tags,
|
|
343
805
|
token_usage: null,
|
|
344
806
|
error: null
|
|
345
807
|
};
|
|
@@ -362,6 +824,7 @@ class HttpTransport {
|
|
|
362
824
|
details: {
|
|
363
825
|
agent_instance_id: this.agentInstanceId,
|
|
364
826
|
schema_name: span.spanType,
|
|
827
|
+
status: apiStatus,
|
|
365
828
|
payload,
|
|
366
829
|
parent_span_id: parentSpanId,
|
|
367
830
|
started_at: startedAt,
|
|
@@ -369,103 +832,51 @@ class HttpTransport {
|
|
|
369
832
|
}
|
|
370
833
|
};
|
|
371
834
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
},
|
|
384
|
-
tool: {
|
|
385
|
-
type: "object",
|
|
386
|
-
properties: { type: { type: "string", const: "tool" } }
|
|
387
|
-
},
|
|
388
|
-
chain: {
|
|
389
|
-
type: "object",
|
|
390
|
-
properties: { type: { type: "string", const: "chain" } }
|
|
391
|
-
},
|
|
392
|
-
retriever: {
|
|
393
|
-
type: "object",
|
|
394
|
-
properties: { type: { type: "string", const: "retriever" } }
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
};
|
|
835
|
+
mapStatusForApi(status) {
|
|
836
|
+
switch (status) {
|
|
837
|
+
case "running":
|
|
838
|
+
return "active";
|
|
839
|
+
case "success":
|
|
840
|
+
return "complete";
|
|
841
|
+
case "error":
|
|
842
|
+
return "failed";
|
|
843
|
+
default:
|
|
844
|
+
return "active";
|
|
845
|
+
}
|
|
398
846
|
}
|
|
399
847
|
async ensureAgentRegistered() {
|
|
400
848
|
if (this.agentInstanceId) {
|
|
401
|
-
return;
|
|
849
|
+
return true;
|
|
402
850
|
}
|
|
403
|
-
const url = `${this.config.apiUrl}/api/v1/agent_instance/register`;
|
|
404
851
|
const payload = {};
|
|
405
852
|
if (this.config.agentId)
|
|
406
853
|
payload.agent_id = this.config.agentId;
|
|
407
|
-
if (this.config.
|
|
854
|
+
if (this.config.agentIdentifier) {
|
|
408
855
|
payload.agent_version = {
|
|
409
|
-
external_identifier: this.config.
|
|
856
|
+
external_identifier: this.config.agentIdentifier,
|
|
410
857
|
name: this.config.agentName || "Agent",
|
|
411
858
|
description: this.config.agentDescription || ""
|
|
412
859
|
};
|
|
413
860
|
}
|
|
414
|
-
if (this.config.
|
|
415
|
-
logger.debug("Skipping schema in registration (skipSchema=true)");
|
|
416
|
-
} else if (this.config.agentSchema) {
|
|
417
|
-
logger.debug("Using custom agent schema");
|
|
861
|
+
if (this.config.agentSchema) {
|
|
418
862
|
payload.agent_schema_version = this.config.agentSchema;
|
|
419
|
-
} else if (this.config.agentSchemaVersion) {
|
|
420
|
-
logger.debug(`Using schema version: ${this.config.agentSchemaVersion}`);
|
|
421
|
-
payload.agent_schema_version = {
|
|
422
|
-
external_identifier: this.config.agentSchemaVersion
|
|
423
|
-
};
|
|
424
|
-
} else {
|
|
425
|
-
logger.debug("Using default hardcoded schema (v1.0.0)");
|
|
426
|
-
payload.agent_schema_version = this.getDefaultSchema();
|
|
427
863
|
}
|
|
428
864
|
try {
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
headers: {
|
|
432
|
-
Authorization: `Bearer ${this.config.apiToken}`,
|
|
433
|
-
"Content-Type": "application/json"
|
|
434
|
-
},
|
|
435
|
-
body: JSON.stringify(payload),
|
|
436
|
-
signal: AbortSignal.timeout(this.config.requestTimeout)
|
|
437
|
-
});
|
|
438
|
-
if (response.ok) {
|
|
439
|
-
const data = await response.json();
|
|
440
|
-
this.agentInstanceId = data?.details?.id ?? null;
|
|
441
|
-
logger.debug(`Registered agent instance: ${this.agentInstanceId}`);
|
|
442
|
-
} else {
|
|
443
|
-
logger.error(`Failed to register agent: ${response.status} ${response.statusText}`);
|
|
444
|
-
}
|
|
865
|
+
const data = await this.agentInstanceClient.register(payload);
|
|
866
|
+
this.agentInstanceId = data.details?.id ?? null;
|
|
445
867
|
} catch (error) {
|
|
446
868
|
logger.error("Error registering agent:", error);
|
|
447
869
|
}
|
|
870
|
+
return this.agentInstanceId !== null;
|
|
448
871
|
}
|
|
449
872
|
async startAgentInstanceHttp() {
|
|
450
|
-
await this.ensureAgentRegistered();
|
|
451
|
-
if (!this.agentInstanceId) {
|
|
873
|
+
const isRegistered = await this.ensureAgentRegistered();
|
|
874
|
+
if (!isRegistered || !this.agentInstanceId) {
|
|
452
875
|
logger.error("Cannot start agent instance: not registered");
|
|
453
876
|
return;
|
|
454
877
|
}
|
|
455
|
-
const url = `${this.config.apiUrl}/api/v1/agent_instance/${this.agentInstanceId}/start`;
|
|
456
878
|
try {
|
|
457
|
-
|
|
458
|
-
method: "POST",
|
|
459
|
-
headers: {
|
|
460
|
-
Authorization: `Bearer ${this.config.apiToken}`,
|
|
461
|
-
"Content-Type": "application/json"
|
|
462
|
-
},
|
|
463
|
-
body: JSON.stringify({}),
|
|
464
|
-
signal: AbortSignal.timeout(this.config.requestTimeout)
|
|
465
|
-
});
|
|
466
|
-
if (!response.ok) {
|
|
467
|
-
logger.error(`Failed to start agent instance: ${response.status} ${response.statusText}`);
|
|
468
|
-
}
|
|
879
|
+
await this.agentInstanceClient.start(this.agentInstanceId);
|
|
469
880
|
} catch (error) {
|
|
470
881
|
logger.error("Error starting agent instance:", error);
|
|
471
882
|
}
|
|
@@ -475,23 +886,12 @@ class HttpTransport {
|
|
|
475
886
|
logger.error("Cannot finish agent instance: not registered");
|
|
476
887
|
return;
|
|
477
888
|
}
|
|
478
|
-
const url = `${this.config.apiUrl}/api/v1/agent_instance/${this.agentInstanceId}/finish`;
|
|
479
889
|
try {
|
|
480
|
-
|
|
481
|
-
method: "POST",
|
|
482
|
-
headers: {
|
|
483
|
-
Authorization: `Bearer ${this.config.apiToken}`,
|
|
484
|
-
"Content-Type": "application/json"
|
|
485
|
-
},
|
|
486
|
-
body: JSON.stringify({}),
|
|
487
|
-
signal: AbortSignal.timeout(this.config.requestTimeout)
|
|
488
|
-
});
|
|
489
|
-
if (!response.ok) {
|
|
490
|
-
logger.error(`Failed to finish agent instance: ${response.status} ${response.statusText}`);
|
|
491
|
-
}
|
|
890
|
+
await this.agentInstanceClient.finish(this.agentInstanceId);
|
|
492
891
|
} catch (error) {
|
|
493
892
|
logger.error("Error finishing agent instance:", error);
|
|
494
893
|
}
|
|
894
|
+
this.agentInstanceId = null;
|
|
495
895
|
}
|
|
496
896
|
async finishSpanHttp(data) {
|
|
497
897
|
const backendSpanId = this.spanIdMap.get(data.spanId);
|
|
@@ -499,35 +899,38 @@ class HttpTransport {
|
|
|
499
899
|
logger.warn(`Cannot finish span ${data.spanId}: backend ID not found`);
|
|
500
900
|
return;
|
|
501
901
|
}
|
|
502
|
-
const url = `${this.config.apiUrl}/api/v1/agent_spans/${backendSpanId}/finish`;
|
|
503
902
|
try {
|
|
504
|
-
|
|
505
|
-
method: "POST",
|
|
506
|
-
headers: {
|
|
507
|
-
Authorization: `Bearer ${this.config.apiToken}`,
|
|
508
|
-
"Content-Type": "application/json"
|
|
509
|
-
},
|
|
510
|
-
body: JSON.stringify({ timestamp: data.timestamp }),
|
|
511
|
-
signal: AbortSignal.timeout(this.config.requestTimeout)
|
|
512
|
-
});
|
|
513
|
-
if (!response.ok) {
|
|
514
|
-
logger.error(`Failed to finish span: ${response.status} ${response.statusText}`);
|
|
515
|
-
}
|
|
903
|
+
await this.agentSpanClient.finish(backendSpanId, data.timestamp);
|
|
516
904
|
} catch (error) {
|
|
517
905
|
logger.error("Error finishing span:", error);
|
|
518
906
|
}
|
|
519
907
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// packages/core/src/create-core.ts
|
|
911
|
+
function createCore(config) {
|
|
912
|
+
if (!config.httpConfig) {
|
|
913
|
+
throw new Error("HTTP transport requires httpConfig to be provided in configuration");
|
|
914
|
+
}
|
|
915
|
+
const httpConfig = HttpTransportConfigSchema.parse(config.httpConfig);
|
|
916
|
+
const transport = new HttpTransport(httpConfig);
|
|
917
|
+
let partition;
|
|
918
|
+
if (config.httpConfig.agentId) {
|
|
919
|
+
try {
|
|
920
|
+
partition = extractPartition(config.httpConfig.agentId);
|
|
921
|
+
} catch {
|
|
922
|
+
partition = undefined;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
const tracer = new Tracer(transport, partition);
|
|
926
|
+
const allowUnregisteredSchema = Boolean(config.httpConfig.agentSchema);
|
|
927
|
+
const agentManager = new AgentInstanceManager(transport, {
|
|
928
|
+
allowUnregisteredSchema
|
|
929
|
+
});
|
|
930
|
+
const shutdown = async () => {
|
|
931
|
+
await tracer.close();
|
|
932
|
+
};
|
|
933
|
+
return { tracer, agentManager, shutdown };
|
|
531
934
|
}
|
|
532
935
|
// packages/core/src/utils/serialization.ts
|
|
533
936
|
function truncateString(value, maxLength) {
|
|
@@ -562,49 +965,22 @@ function serializeValue(value, maxLength = 1e4) {
|
|
|
562
965
|
return `<${typeof value} object>`;
|
|
563
966
|
}
|
|
564
967
|
}
|
|
565
|
-
|
|
566
|
-
// packages/core/src/transport/stdio.ts
|
|
567
|
-
class StdioTransport {
|
|
568
|
-
closed = false;
|
|
569
|
-
writeLock = Promise.resolve();
|
|
570
|
-
emit(span) {
|
|
571
|
-
if (this.closed) {
|
|
572
|
-
return;
|
|
573
|
-
}
|
|
574
|
-
this.writeLock = this.writeLock.then(async () => {
|
|
575
|
-
try {
|
|
576
|
-
const serialized = serializeValue(span);
|
|
577
|
-
const json = JSON.stringify(serialized);
|
|
578
|
-
await Bun.write(Bun.stdout, `${json}
|
|
579
|
-
`);
|
|
580
|
-
} catch (error) {
|
|
581
|
-
console.error("Failed to emit span to stdout:", error);
|
|
582
|
-
}
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
finishSpan() {}
|
|
586
|
-
startAgentInstance() {}
|
|
587
|
-
finishAgentInstance() {}
|
|
588
|
-
async close() {
|
|
589
|
-
this.closed = true;
|
|
590
|
-
await this.writeLock;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
968
|
export {
|
|
594
969
|
truncateString,
|
|
595
970
|
serializeValue,
|
|
596
971
|
getLogger,
|
|
972
|
+
createCore,
|
|
597
973
|
createConfig,
|
|
598
974
|
configureLogging,
|
|
599
975
|
Tracer,
|
|
600
|
-
StdioTransport,
|
|
601
976
|
SpanType,
|
|
602
977
|
SpanStatus,
|
|
603
978
|
SpanContext,
|
|
604
979
|
PartialHttpConfigSchema,
|
|
605
980
|
HttpTransportConfigSchema,
|
|
606
981
|
HttpTransport,
|
|
607
|
-
ConfigSchema
|
|
982
|
+
ConfigSchema,
|
|
983
|
+
AgentInstanceManager
|
|
608
984
|
};
|
|
609
985
|
|
|
610
|
-
//# debugId=
|
|
986
|
+
//# debugId=ED42D0431E54CF4064756E2164756E21
|