@anvia/studio 1.0.1 → 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.
- package/README.md +20 -1
- package/dist/index.d.ts +14 -3
- package/dist/index.js +201 -38
- package/dist/index.js.map +1 -1
- package/dist/ui/assets/{index-Dggzi3lZ.js → index-DmctBqLn.js} +3 -3
- package/dist/ui/assets/{index-Dggzi3lZ.js.map → index-DmctBqLn.js.map} +1 -1
- package/dist/ui/assets/{trace-browser-CwRUyCO6.js → trace-browser-CuBl9__d.js} +2 -2
- package/dist/ui/assets/trace-browser-CuBl9__d.js.map +1 -0
- package/dist/ui/index.html +1 -1
- package/package.json +10 -7
- package/dist/ui/assets/trace-browser-CwRUyCO6.js.map +0 -1
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ const agent = new Agent({
|
|
|
35
35
|
instructions: "Answer support questions clearly.",
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
new Studio([agent]).
|
|
38
|
+
await new Studio([agent]).serve({
|
|
39
39
|
port: 4021,
|
|
40
40
|
});
|
|
41
41
|
```
|
|
@@ -46,6 +46,25 @@ Then open:
|
|
|
46
46
|
http://localhost:4021/ui/playground
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
## Graceful shutdown
|
|
50
|
+
|
|
51
|
+
`serve()` handles both `SIGINT` and `SIGTERM`. Studio stops accepting work, aborts active Agent and
|
|
52
|
+
Pipeline runs, waits for their cancellation observers, and then runs `onShutdown`. Use that callback
|
|
53
|
+
to close observability clients or other caller-owned resources:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
await new Studio([agent]).serve({
|
|
57
|
+
port: 4021,
|
|
58
|
+
shutdownTimeoutMs: 30_000,
|
|
59
|
+
onShutdown: async () => {
|
|
60
|
+
await Promise.all([lens.close(), langfuse.close(), otelSdk.shutdown()]);
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`shutdown()` provides the same draining behavior for application-managed lifecycles. `close()`
|
|
66
|
+
remains synchronous for compatibility: it aborts active work but does not wait for cleanup.
|
|
67
|
+
|
|
49
68
|
## Multi-Provider Models
|
|
50
69
|
|
|
51
70
|
Studio can expose a shared model catalog and let each agent choose from registered providers:
|
package/dist/index.d.ts
CHANGED
|
@@ -581,7 +581,7 @@ type StudioSessionStore = StudioMemoryStore & {
|
|
|
581
581
|
listSessionLogs?(options: StudioSessionLogListOptions): StudioSessionLogEntry[] | Promise<StudioSessionLogEntry[]>;
|
|
582
582
|
deleteSession?(id: string): boolean | Promise<boolean>;
|
|
583
583
|
};
|
|
584
|
-
type StudioTraceStatus = "running" | "success" | "suspended" | "error";
|
|
584
|
+
type StudioTraceStatus = "running" | "success" | "suspended" | "error" | "cancelled";
|
|
585
585
|
type StudioTraceObservationKind = "agent" | "generation" | "tool";
|
|
586
586
|
type StudioTraceObservation = {
|
|
587
587
|
id: string;
|
|
@@ -848,6 +848,7 @@ type StudioServeOptions = {
|
|
|
848
848
|
hostname?: string;
|
|
849
849
|
log?: boolean;
|
|
850
850
|
handleSignals?: boolean;
|
|
851
|
+
shutdownTimeoutMs?: number;
|
|
851
852
|
};
|
|
852
853
|
type StudioServeLifecycleOptions = Omit<StudioServeOptions, "handleSignals"> & {
|
|
853
854
|
signal?: AbortSignal;
|
|
@@ -1011,7 +1012,7 @@ type AgentRunRequest = (AgentRunRequestBase & {
|
|
|
1011
1012
|
});
|
|
1012
1013
|
type AgentRunResponse = Exclude<AgentOutcome, AgentInteractionOutcome> | Omit<AgentInteractionOutcome, "continuation" | "messages">;
|
|
1013
1014
|
type AgentRunStreamEvent = AgentStreamEvent | StudioSessionLogEvent | StudioPipelineLogEvent | StudioPipelineFinalEvent;
|
|
1014
|
-
type StudioErrorCode = "bad_request" | "conflict" | "forbidden" | "not_found" | "payload_too_large" | "unsupported_capability" | "internal_error";
|
|
1015
|
+
type StudioErrorCode = "bad_request" | "conflict" | "forbidden" | "not_found" | "payload_too_large" | "service_unavailable" | "unsupported_capability" | "internal_error";
|
|
1015
1016
|
type StudioErrorResponse = {
|
|
1016
1017
|
error: {
|
|
1017
1018
|
code: StudioErrorCode;
|
|
@@ -1025,6 +1026,9 @@ type AnviaStudio = {
|
|
|
1025
1026
|
fetch(request: Request): Response | Promise<Response>;
|
|
1026
1027
|
config(): StudioConfig;
|
|
1027
1028
|
close(): void;
|
|
1029
|
+
shutdown(options?: {
|
|
1030
|
+
timeoutMs?: number;
|
|
1031
|
+
}): Promise<void>;
|
|
1028
1032
|
};
|
|
1029
1033
|
|
|
1030
1034
|
type StudioTraceObserverOptions = {
|
|
@@ -1042,7 +1046,8 @@ declare class Studio implements AnviaStudio {
|
|
|
1042
1046
|
private studio;
|
|
1043
1047
|
private server;
|
|
1044
1048
|
private websocketServer;
|
|
1045
|
-
private
|
|
1049
|
+
private signalHandlers;
|
|
1050
|
+
private shutdownPromise;
|
|
1046
1051
|
constructor(targets?: StudioTarget[], options?: StudioOptions);
|
|
1047
1052
|
get app(): Hono;
|
|
1048
1053
|
fetch(request: Request): Response | Promise<Response>;
|
|
@@ -1051,6 +1056,12 @@ declare class Studio implements AnviaStudio {
|
|
|
1051
1056
|
start(serveOptions?: StudioServeOptions): this;
|
|
1052
1057
|
serve(serveOptions?: StudioServeLifecycleOptions): Promise<void>;
|
|
1053
1058
|
close(): void;
|
|
1059
|
+
shutdown(options?: {
|
|
1060
|
+
timeoutMs?: number;
|
|
1061
|
+
}): Promise<void>;
|
|
1062
|
+
private shutdownResources;
|
|
1063
|
+
private removeSignalHandlers;
|
|
1064
|
+
private closeNetwork;
|
|
1054
1065
|
private logAddress;
|
|
1055
1066
|
}
|
|
1056
1067
|
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,9 @@ import { serve } from "@hono/node-server";
|
|
|
5
5
|
import { Hono as HonoApp } from "hono";
|
|
6
6
|
import { WebSocketServer } from "ws";
|
|
7
7
|
|
|
8
|
+
// src/traces/trace-observer.ts
|
|
9
|
+
import { AgentRunCancelledError } from "@anvia/core/agent";
|
|
10
|
+
|
|
8
11
|
// src/runtime/json.ts
|
|
9
12
|
function toJsonValue(value) {
|
|
10
13
|
return toJsonValueInternal(value, /* @__PURE__ */ new WeakSet());
|
|
@@ -213,7 +216,7 @@ var StudioRunTraceObserver = class {
|
|
|
213
216
|
});
|
|
214
217
|
}
|
|
215
218
|
async error(args) {
|
|
216
|
-
await this.save("error", {
|
|
219
|
+
await this.save(args.status === "cancelled" ? "cancelled" : "error", {
|
|
217
220
|
endedAt: /* @__PURE__ */ new Date(),
|
|
218
221
|
error: serializeUnknown(args.error),
|
|
219
222
|
usage: args.usage,
|
|
@@ -355,11 +358,12 @@ var ChildAgentToolTraceAccumulator = class {
|
|
|
355
358
|
if (child.type === "error") {
|
|
356
359
|
const metadata = this.childMetadata(agentId, agentName, childTurn);
|
|
357
360
|
if (isRecord(child.usage)) metadata.usage = toJsonValue(child.usage);
|
|
361
|
+
const status = child.error instanceof AgentRunCancelledError ? "cancelled" : "error";
|
|
358
362
|
this.completedObservations.push(
|
|
359
363
|
traceObservation({
|
|
360
364
|
kind: "tool",
|
|
361
365
|
name: `${agentLabel(agentId, agentName)}.error`,
|
|
362
|
-
status
|
|
366
|
+
status,
|
|
363
367
|
turn: this.parent.turn,
|
|
364
368
|
startedAt: /* @__PURE__ */ new Date(),
|
|
365
369
|
error: serializeUnknown(child.error),
|
|
@@ -385,7 +389,7 @@ var ChildAgentToolTraceAccumulator = class {
|
|
|
385
389
|
parentObservationId,
|
|
386
390
|
kind: "agent",
|
|
387
391
|
name: `${agentLabel(agentStart.agentId, agentStart.agentName)}.run`,
|
|
388
|
-
status: agentChildren.some((observation) => observation.status === "error") ? "error" : "success",
|
|
392
|
+
status: agentChildren.some((observation) => observation.status === "error") ? "error" : agentChildren.some((observation) => observation.status === "cancelled") ? "cancelled" : "success",
|
|
389
393
|
turn: this.parent.turn,
|
|
390
394
|
startedAt,
|
|
391
395
|
endedAt,
|
|
@@ -3089,16 +3093,30 @@ function registerAgentRunRoute(app, props) {
|
|
|
3089
3093
|
app.post("/agents/:agentId/runs", (c) => handleAgentRun(c, props));
|
|
3090
3094
|
}
|
|
3091
3095
|
async function handleAgentRun(c, props) {
|
|
3092
|
-
const
|
|
3093
|
-
if (
|
|
3094
|
-
return
|
|
3096
|
+
const lease = props.runLifecycle.start(c.req.raw.signal);
|
|
3097
|
+
if (lease === void 0) {
|
|
3098
|
+
return errorResponse(c, 503, "service_unavailable", "Anvia Studio is shutting down");
|
|
3095
3099
|
}
|
|
3096
|
-
|
|
3097
|
-
|
|
3100
|
+
try {
|
|
3101
|
+
const prepared = await prepareAgentRun(c, props, lease.abortSignal);
|
|
3102
|
+
if (prepared instanceof Response) {
|
|
3103
|
+
lease.finish();
|
|
3104
|
+
return prepared;
|
|
3105
|
+
}
|
|
3106
|
+
if (prepared.body.stream === true) {
|
|
3107
|
+
return handleStreamingAgentRun(c, prepared, props, lease);
|
|
3108
|
+
}
|
|
3109
|
+
try {
|
|
3110
|
+
return await handleBufferedAgentRun(c, prepared, props);
|
|
3111
|
+
} finally {
|
|
3112
|
+
lease.finish();
|
|
3113
|
+
}
|
|
3114
|
+
} catch (error) {
|
|
3115
|
+
lease.finish();
|
|
3116
|
+
throw error;
|
|
3098
3117
|
}
|
|
3099
|
-
return handleBufferedAgentRun(c, prepared, props);
|
|
3100
3118
|
}
|
|
3101
|
-
async function prepareAgentRun(c, props) {
|
|
3119
|
+
async function prepareAgentRun(c, props, abortSignal) {
|
|
3102
3120
|
const agentId = c.req.param("agentId");
|
|
3103
3121
|
const agent = props.agentMap.get(agentId);
|
|
3104
3122
|
if (agent === void 0) {
|
|
@@ -3132,7 +3150,7 @@ async function prepareAgentRun(c, props) {
|
|
|
3132
3150
|
if (body.stream !== void 0) resumedBody = { ...resumedBody, stream: body.stream };
|
|
3133
3151
|
if (body.metadata !== void 0) resumedBody = { ...resumedBody, metadata: body.metadata };
|
|
3134
3152
|
if (body.trace !== void 0) resumedBody = { ...resumedBody, trace: body.trace };
|
|
3135
|
-
const runOptions = createRunOptions(resumedBody, agentId, source.session,
|
|
3153
|
+
const runOptions = createRunOptions(resumedBody, agentId, source.session, abortSignal);
|
|
3136
3154
|
const execution2 = {
|
|
3137
3155
|
generate: (options) => source.runAgent.generate({
|
|
3138
3156
|
continuation: claimed.continuation,
|
|
@@ -3211,7 +3229,7 @@ async function prepareAgentRun(c, props) {
|
|
|
3211
3229
|
execution,
|
|
3212
3230
|
failureMessages: void 0,
|
|
3213
3231
|
memoryCompactionLogged: false,
|
|
3214
|
-
options: createRunOptions(body, agentId, session,
|
|
3232
|
+
options: createRunOptions(body, agentId, session, abortSignal),
|
|
3215
3233
|
runAgent,
|
|
3216
3234
|
runId,
|
|
3217
3235
|
runStartedAt,
|
|
@@ -3339,7 +3357,7 @@ function createRunOptions(body, agentId, session, abortSignal) {
|
|
|
3339
3357
|
}
|
|
3340
3358
|
return options;
|
|
3341
3359
|
}
|
|
3342
|
-
function handleStreamingAgentRun(c, run, props) {
|
|
3360
|
+
function handleStreamingAgentRun(c, run, props, lease) {
|
|
3343
3361
|
const streamOptions = withInternalAgentRunOptions({ ...run.options }, { runId: run.runId });
|
|
3344
3362
|
const runStream = registerStreamingContinuation(
|
|
3345
3363
|
run.execution.stream(streamOptions),
|
|
@@ -3367,7 +3385,7 @@ function handleStreamingAgentRun(c, run, props) {
|
|
|
3367
3385
|
});
|
|
3368
3386
|
stream = persistedRun.events;
|
|
3369
3387
|
}
|
|
3370
|
-
return streamAgentRunEvents(c, stream, {
|
|
3388
|
+
return streamAgentRunEvents(c, finishRunLease(stream, lease), {
|
|
3371
3389
|
runId: run.runId,
|
|
3372
3390
|
onCancel: async () => {
|
|
3373
3391
|
const persistence = [];
|
|
@@ -3386,6 +3404,13 @@ function handleStreamingAgentRun(c, run, props) {
|
|
|
3386
3404
|
}
|
|
3387
3405
|
});
|
|
3388
3406
|
}
|
|
3407
|
+
async function* finishRunLease(events, lease) {
|
|
3408
|
+
try {
|
|
3409
|
+
yield* events;
|
|
3410
|
+
} finally {
|
|
3411
|
+
lease.finish();
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3389
3414
|
async function handleBufferedAgentRun(c, run, props) {
|
|
3390
3415
|
try {
|
|
3391
3416
|
const runtimeOptions = await startBufferedSessionRun(run);
|
|
@@ -3680,7 +3705,7 @@ function parseTraceStatus(value) {
|
|
|
3680
3705
|
if (status === void 0) {
|
|
3681
3706
|
return void 0;
|
|
3682
3707
|
}
|
|
3683
|
-
return status === "running" || status === "success" || status === "suspended" || status === "error" ? status : false;
|
|
3708
|
+
return status === "running" || status === "success" || status === "suspended" || status === "error" || status === "cancelled" ? status : false;
|
|
3684
3709
|
}
|
|
3685
3710
|
function parseAfter(value) {
|
|
3686
3711
|
if (value === void 0 || value.trim().length === 0) {
|
|
@@ -4997,7 +5022,7 @@ function registerPipelineRoutes(app, props) {
|
|
|
4997
5022
|
if ("error" in body) {
|
|
4998
5023
|
return body.error;
|
|
4999
5024
|
}
|
|
5000
|
-
return
|
|
5025
|
+
return executeTrackedPipelineRun(c, props, pipeline, body);
|
|
5001
5026
|
});
|
|
5002
5027
|
app.post("/pipelines/:pipelineId/runs/:runId/replay", async (c) => {
|
|
5003
5028
|
const pipeline = props.pipelineMap.get(c.req.param("pipelineId"));
|
|
@@ -5033,10 +5058,22 @@ function registerPipelineRoutes(app, props) {
|
|
|
5033
5058
|
metadata: replayMetadata(sourceRun.metadata, body.metadata, sourceRun.runId)
|
|
5034
5059
|
};
|
|
5035
5060
|
if (body.stream !== void 0) replayRequest.stream = body.stream;
|
|
5036
|
-
return
|
|
5061
|
+
return executeTrackedPipelineRun(c, props, pipeline, replayRequest);
|
|
5037
5062
|
});
|
|
5038
5063
|
}
|
|
5039
|
-
async function
|
|
5064
|
+
async function executeTrackedPipelineRun(c, props, pipeline, body) {
|
|
5065
|
+
const lease = props.runLifecycle.start(c.req.raw.signal);
|
|
5066
|
+
if (lease === void 0) {
|
|
5067
|
+
return errorResponse(c, 503, "service_unavailable", "Anvia Studio is shutting down");
|
|
5068
|
+
}
|
|
5069
|
+
try {
|
|
5070
|
+
return await executePipelineRun(c, props, pipeline, body, lease);
|
|
5071
|
+
} catch (error) {
|
|
5072
|
+
lease.finish();
|
|
5073
|
+
throw error;
|
|
5074
|
+
}
|
|
5075
|
+
}
|
|
5076
|
+
async function executePipelineRun(c, props, pipeline, body, lease) {
|
|
5040
5077
|
const runId = globalThis.crypto.randomUUID();
|
|
5041
5078
|
const startedAt = Date.now();
|
|
5042
5079
|
const startedAtIso = new Date(startedAt).toISOString();
|
|
@@ -5063,12 +5100,13 @@ async function executePipelineRun(c, props, pipeline, body) {
|
|
|
5063
5100
|
runId,
|
|
5064
5101
|
input: body.input,
|
|
5065
5102
|
startedAt,
|
|
5066
|
-
startedAtIso
|
|
5103
|
+
startedAtIso,
|
|
5104
|
+
onFinish: lease.finish
|
|
5067
5105
|
};
|
|
5068
5106
|
if (body.metadata !== void 0) streamOptions.metadata = body.metadata;
|
|
5069
5107
|
if (props.logStore !== void 0) streamOptions.logStore = props.logStore;
|
|
5070
5108
|
if (props.runStore !== void 0) streamOptions.runStore = props.runStore;
|
|
5071
|
-
streamOptions.abortSignal =
|
|
5109
|
+
streamOptions.abortSignal = lease.abortSignal;
|
|
5072
5110
|
return streamPipelineRun(c, streamOptions);
|
|
5073
5111
|
}
|
|
5074
5112
|
try {
|
|
@@ -5077,7 +5115,7 @@ async function executePipelineRun(c, props, pipeline, body) {
|
|
|
5077
5115
|
input: body.input,
|
|
5078
5116
|
runId,
|
|
5079
5117
|
metadata: body.metadata,
|
|
5080
|
-
abortSignal:
|
|
5118
|
+
abortSignal: lease.abortSignal,
|
|
5081
5119
|
observer: {
|
|
5082
5120
|
async onEvent(event) {
|
|
5083
5121
|
await appendPipelineLog(props.logStore, pipelineStageLog(event, pipeline.id));
|
|
@@ -5134,6 +5172,8 @@ async function executePipelineRun(c, props, pipeline, body) {
|
|
|
5134
5172
|
pipelineRunFailedLog(pipeline.id, runId, error, startedAt)
|
|
5135
5173
|
);
|
|
5136
5174
|
return errorResponse(c, 500, "internal_error", "Pipeline run failed", serializeError(error));
|
|
5175
|
+
} finally {
|
|
5176
|
+
lease.finish();
|
|
5137
5177
|
}
|
|
5138
5178
|
}
|
|
5139
5179
|
function pipelineDetail(pipeline) {
|
|
@@ -5232,7 +5272,11 @@ async function* pipelineRunEvents(props) {
|
|
|
5232
5272
|
yield next.value;
|
|
5233
5273
|
}
|
|
5234
5274
|
} finally {
|
|
5235
|
-
|
|
5275
|
+
try {
|
|
5276
|
+
await run;
|
|
5277
|
+
} finally {
|
|
5278
|
+
props.onFinish();
|
|
5279
|
+
}
|
|
5236
5280
|
}
|
|
5237
5281
|
}
|
|
5238
5282
|
async function savePipelineRun(store, input) {
|
|
@@ -5320,6 +5364,61 @@ function parsePipelineLogAfter(value) {
|
|
|
5320
5364
|
return after;
|
|
5321
5365
|
}
|
|
5322
5366
|
|
|
5367
|
+
// src/runtime/run-lifecycle.ts
|
|
5368
|
+
var StudioRunLifecycle = class {
|
|
5369
|
+
shutdownController = new AbortController();
|
|
5370
|
+
active = /* @__PURE__ */ new Set();
|
|
5371
|
+
drainPromise;
|
|
5372
|
+
resolveDrain;
|
|
5373
|
+
closed = false;
|
|
5374
|
+
start(requestSignal) {
|
|
5375
|
+
if (this.closed) return void 0;
|
|
5376
|
+
const token = /* @__PURE__ */ Symbol("studio-run");
|
|
5377
|
+
this.active.add(token);
|
|
5378
|
+
let finished = false;
|
|
5379
|
+
return {
|
|
5380
|
+
abortSignal: AbortSignal.any([requestSignal, this.shutdownController.signal]),
|
|
5381
|
+
finish: () => {
|
|
5382
|
+
if (finished) return;
|
|
5383
|
+
finished = true;
|
|
5384
|
+
this.active.delete(token);
|
|
5385
|
+
if (this.active.size === 0) {
|
|
5386
|
+
this.resolveDrain?.();
|
|
5387
|
+
this.resolveDrain = void 0;
|
|
5388
|
+
this.drainPromise = void 0;
|
|
5389
|
+
}
|
|
5390
|
+
}
|
|
5391
|
+
};
|
|
5392
|
+
}
|
|
5393
|
+
close() {
|
|
5394
|
+
if (this.closed) return;
|
|
5395
|
+
this.closed = true;
|
|
5396
|
+
this.shutdownController.abort(new Error("Anvia Studio is shutting down."));
|
|
5397
|
+
}
|
|
5398
|
+
async drain(timeoutMs) {
|
|
5399
|
+
this.close();
|
|
5400
|
+
if (this.active.size === 0) return;
|
|
5401
|
+
this.drainPromise ??= new Promise((resolve2) => {
|
|
5402
|
+
this.resolveDrain = resolve2;
|
|
5403
|
+
});
|
|
5404
|
+
await withTimeout(this.drainPromise, timeoutMs);
|
|
5405
|
+
}
|
|
5406
|
+
};
|
|
5407
|
+
async function withTimeout(promise, timeoutMs) {
|
|
5408
|
+
let timer;
|
|
5409
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
5410
|
+
timer = setTimeout(
|
|
5411
|
+
() => reject(new Error(`Anvia Studio shutdown timed out after ${timeoutMs}ms.`)),
|
|
5412
|
+
timeoutMs
|
|
5413
|
+
);
|
|
5414
|
+
});
|
|
5415
|
+
try {
|
|
5416
|
+
await Promise.race([promise, timeout]);
|
|
5417
|
+
} finally {
|
|
5418
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
5419
|
+
}
|
|
5420
|
+
}
|
|
5421
|
+
|
|
5323
5422
|
// src/runtime/sandbox-views.ts
|
|
5324
5423
|
import { upgradeWebSocket } from "@hono/node-server";
|
|
5325
5424
|
import { getConnInfo } from "@hono/node-server/conninfo";
|
|
@@ -6810,7 +6909,8 @@ var Studio = class {
|
|
|
6810
6909
|
studio;
|
|
6811
6910
|
server;
|
|
6812
6911
|
websocketServer;
|
|
6813
|
-
|
|
6912
|
+
signalHandlers;
|
|
6913
|
+
shutdownPromise;
|
|
6814
6914
|
constructor(targets = [], options = {}) {
|
|
6815
6915
|
this.options = studioOptionsFromTargets(targets, options);
|
|
6816
6916
|
this.studio = createStudioApp(this.options);
|
|
@@ -6832,6 +6932,7 @@ var Studio = class {
|
|
|
6832
6932
|
start(serveOptions = {}) {
|
|
6833
6933
|
this.close();
|
|
6834
6934
|
this.studio = createStudioApp(this.options);
|
|
6935
|
+
this.shutdownPromise = void 0;
|
|
6835
6936
|
const port = serveOptions.port ?? Number(process.env.RUNNER_PORT ?? 4021);
|
|
6836
6937
|
const serverOptions = {
|
|
6837
6938
|
fetch: (request, env) => this.studio.app.fetch(request, env),
|
|
@@ -6851,16 +6952,27 @@ var Studio = class {
|
|
|
6851
6952
|
this.logAddress(host, port);
|
|
6852
6953
|
}
|
|
6853
6954
|
if (serveOptions.handleSignals ?? true) {
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6955
|
+
const shutdownFor = (signal) => {
|
|
6956
|
+
process.exitCode = signal === "SIGINT" ? 130 : 143;
|
|
6957
|
+
void this.shutdown(shutdownOptions(serveOptions.shutdownTimeoutMs)).catch(
|
|
6958
|
+
(error) => {
|
|
6959
|
+
console.error(error);
|
|
6960
|
+
process.exitCode = 1;
|
|
6961
|
+
}
|
|
6962
|
+
);
|
|
6963
|
+
};
|
|
6964
|
+
this.signalHandlers = {
|
|
6965
|
+
sigint: () => shutdownFor("SIGINT"),
|
|
6966
|
+
sigterm: () => shutdownFor("SIGTERM")
|
|
6857
6967
|
};
|
|
6858
|
-
process.once("SIGINT", this.
|
|
6968
|
+
process.once("SIGINT", this.signalHandlers.sigint);
|
|
6969
|
+
process.once("SIGTERM", this.signalHandlers.sigterm);
|
|
6859
6970
|
}
|
|
6860
6971
|
return this;
|
|
6861
6972
|
}
|
|
6862
6973
|
async serve(serveOptions = {}) {
|
|
6863
|
-
const { onShutdown, signal, ...startOptions } = serveOptions;
|
|
6974
|
+
const { onShutdown, shutdownTimeoutMs, signal, ...startOptions } = serveOptions;
|
|
6975
|
+
const failures = [];
|
|
6864
6976
|
try {
|
|
6865
6977
|
this.start({ ...startOptions, log: false, handleSignals: false });
|
|
6866
6978
|
const server = this.server;
|
|
@@ -6875,21 +6987,55 @@ var Studio = class {
|
|
|
6875
6987
|
this.logAddress(startOptions.hostname ?? "localhost", port);
|
|
6876
6988
|
}
|
|
6877
6989
|
await waitForShutdown(signal);
|
|
6878
|
-
}
|
|
6879
|
-
|
|
6990
|
+
} catch (error) {
|
|
6991
|
+
failures.push(error);
|
|
6992
|
+
}
|
|
6993
|
+
try {
|
|
6994
|
+
await this.shutdown(shutdownOptions(shutdownTimeoutMs));
|
|
6995
|
+
} catch (error) {
|
|
6996
|
+
failures.push(error);
|
|
6997
|
+
}
|
|
6998
|
+
try {
|
|
6880
6999
|
await onShutdown?.();
|
|
7000
|
+
} catch (error) {
|
|
7001
|
+
failures.push(error);
|
|
7002
|
+
}
|
|
7003
|
+
if (failures.length === 1) throw failures[0];
|
|
7004
|
+
if (failures.length > 1) {
|
|
7005
|
+
throw new AggregateError(failures, "Failed to run and shut down Anvia Studio.");
|
|
6881
7006
|
}
|
|
6882
7007
|
}
|
|
6883
7008
|
close() {
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
7009
|
+
this.removeSignalHandlers();
|
|
7010
|
+
this.closeNetwork();
|
|
7011
|
+
this.studio.close();
|
|
7012
|
+
}
|
|
7013
|
+
shutdown(options = {}) {
|
|
7014
|
+
const timeoutMs = options.timeoutMs ?? 3e4;
|
|
7015
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
7016
|
+
return Promise.reject(
|
|
7017
|
+
new TypeError("Anvia Studio shutdown timeoutMs must be a positive number.")
|
|
7018
|
+
);
|
|
6887
7019
|
}
|
|
7020
|
+
this.shutdownPromise ??= this.shutdownResources(timeoutMs);
|
|
7021
|
+
return this.shutdownPromise;
|
|
7022
|
+
}
|
|
7023
|
+
async shutdownResources(timeoutMs) {
|
|
7024
|
+
this.removeSignalHandlers();
|
|
7025
|
+
this.closeNetwork();
|
|
7026
|
+
await this.studio.shutdown({ timeoutMs });
|
|
7027
|
+
}
|
|
7028
|
+
removeSignalHandlers() {
|
|
7029
|
+
if (this.signalHandlers === void 0) return;
|
|
7030
|
+
process.off("SIGINT", this.signalHandlers.sigint);
|
|
7031
|
+
process.off("SIGTERM", this.signalHandlers.sigterm);
|
|
7032
|
+
this.signalHandlers = void 0;
|
|
7033
|
+
}
|
|
7034
|
+
closeNetwork() {
|
|
6888
7035
|
this.server?.close();
|
|
6889
7036
|
this.server = void 0;
|
|
6890
7037
|
this.websocketServer?.close();
|
|
6891
7038
|
this.websocketServer = void 0;
|
|
6892
|
-
this.studio.close();
|
|
6893
7039
|
}
|
|
6894
7040
|
logAddress(host, port) {
|
|
6895
7041
|
if (isStudioUiEnabled(this.options.ui)) {
|
|
@@ -6900,6 +7046,9 @@ var Studio = class {
|
|
|
6900
7046
|
}
|
|
6901
7047
|
}
|
|
6902
7048
|
};
|
|
7049
|
+
function shutdownOptions(timeoutMs) {
|
|
7050
|
+
return timeoutMs === void 0 ? {} : { timeoutMs };
|
|
7051
|
+
}
|
|
6903
7052
|
function waitForServerListening(server) {
|
|
6904
7053
|
if (server.listening) return Promise.resolve();
|
|
6905
7054
|
return new Promise((resolve2, reject) => {
|
|
@@ -7045,6 +7194,7 @@ function createStudioApp(options) {
|
|
|
7045
7194
|
const pipelineMap = new Map(pipelines.map((pipeline) => [pipeline.id, pipeline]));
|
|
7046
7195
|
const evalMap = new Map(options.evals.map((suite) => [suite.id ?? suite.name, suite]));
|
|
7047
7196
|
const continuationRegistry = createStudioContinuationRegistry();
|
|
7197
|
+
const runLifecycle = new StudioRunLifecycle();
|
|
7048
7198
|
const sandboxRegistry = createStudioSandboxRegistry(agents, options.sandboxes ?? []);
|
|
7049
7199
|
const memorySources = createStudioMemorySourceRegistry(agents, stores.sessions);
|
|
7050
7200
|
const app = new HonoApp();
|
|
@@ -7107,7 +7257,8 @@ function createStudioApp(options) {
|
|
|
7107
7257
|
registerKnowledgeRoutes(app, knowledgeOptions);
|
|
7108
7258
|
const pipelineOptions = {
|
|
7109
7259
|
pipelines,
|
|
7110
|
-
pipelineMap
|
|
7260
|
+
pipelineMap,
|
|
7261
|
+
runLifecycle
|
|
7111
7262
|
};
|
|
7112
7263
|
if (stores.pipelineLogs !== void 0) pipelineOptions.logStore = stores.pipelineLogs;
|
|
7113
7264
|
if (stores.pipelineRuns !== void 0) pipelineOptions.runStore = stores.pipelineRuns;
|
|
@@ -7116,7 +7267,8 @@ function createStudioApp(options) {
|
|
|
7116
7267
|
agentMap,
|
|
7117
7268
|
stores,
|
|
7118
7269
|
modelRegistry,
|
|
7119
|
-
continuationRegistry
|
|
7270
|
+
continuationRegistry,
|
|
7271
|
+
runLifecycle
|
|
7120
7272
|
});
|
|
7121
7273
|
if (memorySources.size > 0 || stores.sessions !== void 0) {
|
|
7122
7274
|
registerMemoryRoutes(app, {
|
|
@@ -7139,6 +7291,14 @@ function createStudioApp(options) {
|
|
|
7139
7291
|
app.all(`/${capability}`, (c) => unsupportedCapability(c, capability));
|
|
7140
7292
|
app.all(`/${capability}/*`, (c) => unsupportedCapability(c, capability));
|
|
7141
7293
|
}
|
|
7294
|
+
let closing = false;
|
|
7295
|
+
const beginClose = () => {
|
|
7296
|
+
if (closing) return;
|
|
7297
|
+
closing = true;
|
|
7298
|
+
continuationRegistry.clear();
|
|
7299
|
+
closeSandboxViews();
|
|
7300
|
+
runLifecycle.close();
|
|
7301
|
+
};
|
|
7142
7302
|
const studio = {
|
|
7143
7303
|
app,
|
|
7144
7304
|
fetch(request) {
|
|
@@ -7148,8 +7308,11 @@ function createStudioApp(options) {
|
|
|
7148
7308
|
return buildConfig(options, agents, pipelines, stores, sandboxRegistry.size);
|
|
7149
7309
|
},
|
|
7150
7310
|
close() {
|
|
7151
|
-
|
|
7152
|
-
|
|
7311
|
+
beginClose();
|
|
7312
|
+
},
|
|
7313
|
+
async shutdown(shutdownOptions2 = {}) {
|
|
7314
|
+
beginClose();
|
|
7315
|
+
await runLifecycle.drain(shutdownOptions2.timeoutMs ?? 3e4);
|
|
7153
7316
|
}
|
|
7154
7317
|
};
|
|
7155
7318
|
if (stores.sessions !== void 0) Object.assign(studio, { sessionStore: stores.sessions });
|