@toren-run/core 0.1.1 → 0.1.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/dist/loop.js +12 -2
- package/dist/orchestrator.js +3 -2
- package/dist/worker.d.ts +2 -0
- package/dist/worker.js +14 -1
- package/package.json +1 -1
package/dist/loop.js
CHANGED
|
@@ -129,14 +129,24 @@ async function runTaskLoopImpl(args) {
|
|
|
129
129
|
else {
|
|
130
130
|
// Crash window: call was issued but the response never landed. Re-issue (at-least-once).
|
|
131
131
|
ptr += 1;
|
|
132
|
-
response = await withSpan("toren.llm", { "gen_ai.request.model": request.model }, () =>
|
|
132
|
+
response = await withSpan("toren.llm", { "gen_ai.request.model": request.model }, async (span) => {
|
|
133
|
+
const r = await provider.complete(request);
|
|
134
|
+
if (r.usage)
|
|
135
|
+
span.setAttributes({ "gen_ai.usage.input_tokens": r.usage.inputTokens, "gen_ai.usage.output_tokens": r.usage.outputTokens });
|
|
136
|
+
return r;
|
|
137
|
+
});
|
|
133
138
|
await append([ev("LlmCallCompleted", { stepId: next.payload.stepId, response, usage: response.usage })]);
|
|
134
139
|
}
|
|
135
140
|
}
|
|
136
141
|
else {
|
|
137
142
|
const stepId = `s${head + 1}`;
|
|
138
143
|
await append([ev("LlmCallStarted", { stepId, requestDigest: digest, model: request.model })]);
|
|
139
|
-
response = await withSpan("toren.llm", { "gen_ai.request.model": request.model }, () =>
|
|
144
|
+
response = await withSpan("toren.llm", { "gen_ai.request.model": request.model }, async (span) => {
|
|
145
|
+
const r = await provider.complete(request);
|
|
146
|
+
if (r.usage)
|
|
147
|
+
span.setAttributes({ "gen_ai.usage.input_tokens": r.usage.inputTokens, "gen_ai.usage.output_tokens": r.usage.outputTokens });
|
|
148
|
+
return r;
|
|
149
|
+
});
|
|
140
150
|
await append([ev("LlmCallCompleted", { stepId, response, usage: response.usage })]);
|
|
141
151
|
}
|
|
142
152
|
if (response.usage) {
|
package/dist/orchestrator.js
CHANGED
|
@@ -107,14 +107,15 @@ async function tickImpl(deps, runId) {
|
|
|
107
107
|
const r = await deps.store.append(runId, "run", session.head, [ev("RunCompleted", { output })]);
|
|
108
108
|
if (!r.ok)
|
|
109
109
|
throw new RunLeaseLostError("run stream advanced concurrently");
|
|
110
|
-
await deps.store.updateRun(runId, { status: "completed", output });
|
|
110
|
+
await deps.store.updateRun(runId, { status: "completed", output, error: null });
|
|
111
111
|
await flush();
|
|
112
112
|
return "completed";
|
|
113
113
|
}
|
|
114
114
|
catch (e) {
|
|
115
115
|
if (e instanceof WorkflowBlocked) {
|
|
116
116
|
await flush();
|
|
117
|
-
|
|
117
|
+
// Parking cleanly is progress: a transient error recorded by a worker retry is stale now.
|
|
118
|
+
await deps.store.updateRun(runId, { status: "running", error: null });
|
|
118
119
|
return "blocked";
|
|
119
120
|
}
|
|
120
121
|
if (e instanceof RunLeaseLostError)
|
package/dist/worker.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { type TickDeps } from "./orchestrator.js";
|
|
2
|
+
/** Exponential backoff for task retries: 0.2s doubling per attempt, capped at 60s. */
|
|
3
|
+
export declare const retryDelaySeconds: (attempt: number) => number;
|
|
2
4
|
export interface WorkerOpts {
|
|
3
5
|
concurrency?: number;
|
|
4
6
|
visibilitySeconds?: number;
|
package/dist/worker.js
CHANGED
|
@@ -2,6 +2,8 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { InvalidationStormError, runTaskLoop, TaskLeaseLostError } from "./loop.js";
|
|
3
3
|
import { findTaskSpec, tick } from "./orchestrator.js";
|
|
4
4
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
5
|
+
/** Exponential backoff for task retries: 0.2s doubling per attempt, capped at 60s. */
|
|
6
|
+
export const retryDelaySeconds = (attempt) => Math.min(60, 0.2 * 2 ** Math.max(0, attempt - 1));
|
|
5
7
|
/**
|
|
6
8
|
* Local worker runtime: in-process pollers over the orchestrator and task
|
|
7
9
|
* queues (local binding). One instance serves one agent or a whole
|
|
@@ -165,7 +167,18 @@ export class LocalWorkerRuntime {
|
|
|
165
167
|
await this.shared.queue.nack(d, { delaySeconds: 20 });
|
|
166
168
|
return;
|
|
167
169
|
}
|
|
168
|
-
|
|
170
|
+
// An outside dependency failed (a provider, a tool, the network). The
|
|
171
|
+
// retry is durability doing its job; the reason must not vanish into
|
|
172
|
+
// it. Record it on the run so jobs/console answer "why is it stuck"
|
|
173
|
+
// while the retries continue; a later success clears it.
|
|
174
|
+
try {
|
|
175
|
+
const reason = e instanceof Error ? e.message : String(e);
|
|
176
|
+
await deps.store.updateRun(msg.runId, {
|
|
177
|
+
error: `${reason}${msg.taskId ? ` (task ${msg.taskId}, attempt ${d.attempt})` : ""}`,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
catch { /* the store may be the thing that failed */ }
|
|
181
|
+
await this.shared.queue.nack(d, { delaySeconds: retryDelaySeconds(d.attempt) });
|
|
169
182
|
}
|
|
170
183
|
catch {
|
|
171
184
|
// Queue unreachable too: do nothing. The visibility timeout redelivers
|