@cubicecho/agent-core 1.2.0 → 1.3.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/README.md +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/run-turn.d.ts +38 -0
- package/dist/run-turn.js +33 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,6 +29,7 @@ only, Node >=22.
|
|
|
29
29
|
| `client` | A pooled `OpenAI` client per endpoint, plus the context-window listing and its cache. |
|
|
30
30
|
| `retry` | What to do when a request is lost, refused or too big: `isTransient`, `backoffMs`, `ContextOverflow`, `EndpointSilent`. |
|
|
31
31
|
| `config` | The structural interfaces every function here asks for. |
|
|
32
|
+
| `run-turn` | `runTurn`: one turn with the retry loop around the negotiation around the stream. The whole loop, for a caller that wants it rather than its parts. |
|
|
32
33
|
| `reset` | `resetAll`: drops every cache and latch in one call, so a teardown cannot forget one. |
|
|
33
34
|
| `errors` | `errorMessage`: a caught `unknown` turned into something a run row can hold. |
|
|
34
35
|
| `catalog` | `CatalogServer`: the name-only shape `tool-loading` reads a connected server as. |
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export { errorMessage } from "./errors.ts";
|
|
|
17
17
|
export { emit, endRun, fold, history, type RunEvent, type RunEventInput, type RunEventKind, type RunUsage, reset, watch, } from "./events.ts";
|
|
18
18
|
export { resetAll } from "./reset.ts";
|
|
19
19
|
export { backoffMs, ContextOverflow, compact, EndpointSilent, isOverflow, isTransient, requestTokens, SMALLEST_LIKELY_WINDOW, sleep, } from "./retry.ts";
|
|
20
|
+
export { type RunTurnOptions, runTurn } from "./run-turn.ts";
|
|
20
21
|
export { isGrammarError, relaxTools, sanitizeTools } from "./schema-compat.ts";
|
|
21
22
|
export { ask, clean, listLines, parseJson, resetHints, type SideTaskOptions, tryAsk, } from "./side-task.ts";
|
|
22
23
|
export { type Produced, type StreamTurnOptions, streamTurn, type Turn, type TurnUsage, } from "./stream.ts";
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ export { errorMessage } from "./errors.js";
|
|
|
15
15
|
export { emit, endRun, fold, history, reset, watch, } from "./events.js";
|
|
16
16
|
export { resetAll } from "./reset.js";
|
|
17
17
|
export { backoffMs, ContextOverflow, compact, EndpointSilent, isOverflow, isTransient, requestTokens, SMALLEST_LIKELY_WINDOW, sleep, } from "./retry.js";
|
|
18
|
+
export { runTurn } from "./run-turn.js";
|
|
18
19
|
export { isGrammarError, relaxTools, sanitizeTools } from "./schema-compat.js";
|
|
19
20
|
export { ask, clean, listLines, parseJson, resetHints, tryAsk, } from "./side-task.js";
|
|
20
21
|
export { streamTurn, } from "./stream.js";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type OpenAI from "openai";
|
|
2
|
+
import { type Capabilities } from "./capabilities.ts";
|
|
3
|
+
import { type StreamTurnOptions, type Turn } from "./stream.ts";
|
|
4
|
+
/**
|
|
5
|
+
* One turn, given as many attempts as the caller allows.
|
|
6
|
+
*
|
|
7
|
+
* Two different things are being recovered from here, and they nest. The inner one is a
|
|
8
|
+
* capability the endpoint turns out not to have — `stream_options`, a grammar keyword — which
|
|
9
|
+
* is a refusal: it is answered by sending a lesser request, and it latches against that
|
|
10
|
+
* endpoint for the life of the process, so it costs one failed call rather than one a run.
|
|
11
|
+
* The outer one is the endpoint being unreachable, busy or silent, which is not about this
|
|
12
|
+
* request at all and is worth simply waiting out.
|
|
13
|
+
*
|
|
14
|
+
* Both are bounded by the same rule: nothing is sent again once the server has started
|
|
15
|
+
* answering. The tokens are already out and on their way to whoever is watching, and a second
|
|
16
|
+
* attempt would say everything twice. That is what `produced` is, one box per attempt.
|
|
17
|
+
*/
|
|
18
|
+
/** A retry is not the same event as a downgrade, but a watcher wants to be told about both. */
|
|
19
|
+
export interface RunTurnOptions extends Omit<StreamTurnOptions, "produced"> {
|
|
20
|
+
/**
|
|
21
|
+
* How many times a lost request is worth sending again. Zero is one attempt, which is the
|
|
22
|
+
* default because a caller with no retry budget in its settings should not inherit one.
|
|
23
|
+
* A downgrade does not spend an attempt: it is a different request, not the same one again.
|
|
24
|
+
*/
|
|
25
|
+
maxRetries?: number;
|
|
26
|
+
/**
|
|
27
|
+
* Told what was given up on and what is being waited out, for a watcher who would otherwise
|
|
28
|
+
* see an unexplained pause. Carries both the capability notices and the retry notices.
|
|
29
|
+
*/
|
|
30
|
+
onNotice?: (message: string) => void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* `request` is a callback rather than a body because the body has to be rebuilt from whatever
|
|
34
|
+
* the last attempt latched off: the tools it sends depend on `strictSchemas`, and `relaxTools`
|
|
35
|
+
* has to apply to the schemas that were just sanitised. It is handed the same `Capabilities`
|
|
36
|
+
* object throughout, and a caller that reads those from its own closure can ignore the argument.
|
|
37
|
+
*/
|
|
38
|
+
export declare function runTurn(client: OpenAI, supports: Capabilities, request: (supports: Capabilities) => OpenAI.ChatCompletionCreateParamsStreaming, { maxRetries, onNotice, ...stream }?: RunTurnOptions): Promise<Turn>;
|
package/dist/run-turn.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { negotiate } from "./capabilities.js";
|
|
2
|
+
import { errorMessage } from "./errors.js";
|
|
3
|
+
import { backoffMs, isTransient, sleep } from "./retry.js";
|
|
4
|
+
import { streamTurn } from "./stream.js";
|
|
5
|
+
/**
|
|
6
|
+
* `request` is a callback rather than a body because the body has to be rebuilt from whatever
|
|
7
|
+
* the last attempt latched off: the tools it sends depend on `strictSchemas`, and `relaxTools`
|
|
8
|
+
* has to apply to the schemas that were just sanitised. It is handed the same `Capabilities`
|
|
9
|
+
* object throughout, and a caller that reads those from its own closure can ignore the argument.
|
|
10
|
+
*/
|
|
11
|
+
export async function runTurn(client, supports, request, { maxRetries = 0, onNotice, ...stream } = {}) {
|
|
12
|
+
for (let attempt = 0;; attempt++) {
|
|
13
|
+
const produced = { any: false };
|
|
14
|
+
try {
|
|
15
|
+
return await negotiate(supports, (capabilities, box) => streamTurn(client, request(capabilities), { ...stream, produced: box }), { produced, onNotice });
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
// The abort is read before the classification, not after. A run stopped by its operator
|
|
19
|
+
// can trip the idle watchdog on the way out, and `EndpointSilent` is transient by the
|
|
20
|
+
// rules in `retry.ts` — so classifying first brings a cancelled run back from the dead.
|
|
21
|
+
if (produced.any || stream.signal?.aborted)
|
|
22
|
+
throw error;
|
|
23
|
+
if (attempt >= maxRetries || !isTransient(error))
|
|
24
|
+
throw error;
|
|
25
|
+
const wait = backoffMs(attempt);
|
|
26
|
+
// Reported in whatever unit reads as a number: the first backoff is under a second, and
|
|
27
|
+
// "retrying in 0s" is what rounding it to seconds says.
|
|
28
|
+
const delay = wait < 1000 ? `${Math.round(wait)}ms` : `${Math.round(wait / 1000)}s`;
|
|
29
|
+
onNotice?.(`${errorMessage(error)} — retrying in ${delay} (${attempt + 1}/${maxRetries})`);
|
|
30
|
+
await sleep(wait, stream.signal);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cubicecho/agent-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "The endpoint-agnostic half of an OpenAI-compatible agent loop: tool-schema compatibility, on-demand tool loading, one-shot side tasks, run events, and a pooled client.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openai",
|