@earendil-works/pi-durable 1.2.0 → 1.4.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/CHANGELOG.md +56 -0
- package/README.md +128 -3
- package/dist/codemode/codemode.d.ts.map +1 -1
- package/dist/codemode/codemode.js +12 -4
- package/dist/codemode/codemode.js.map +1 -1
- package/dist/codemode/models.d.ts.map +1 -1
- package/dist/codemode/models.js +2 -0
- package/dist/codemode/models.js.map +1 -1
- package/dist/documents.d.ts +1 -1
- package/dist/documents.d.ts.map +1 -1
- package/dist/documents.js.map +1 -1
- package/dist/env/index.d.ts +1 -0
- package/dist/env/index.d.ts.map +1 -1
- package/dist/env/index.js.map +1 -1
- package/dist/env/node.d.ts.map +1 -1
- package/dist/env/node.js +11 -3
- package/dist/env/node.js.map +1 -1
- package/dist/harness/agent.d.ts +13 -2
- package/dist/harness/agent.d.ts.map +1 -1
- package/dist/harness/agent.js +39 -5
- package/dist/harness/agent.js.map +1 -1
- package/dist/harness/compaction.d.ts +3 -1
- package/dist/harness/compaction.d.ts.map +1 -1
- package/dist/harness/compaction.js +19 -4
- package/dist/harness/compaction.js.map +1 -1
- package/dist/harness/context.d.ts +5 -0
- package/dist/harness/context.d.ts.map +1 -1
- package/dist/harness/context.js +46 -12
- package/dist/harness/context.js.map +1 -1
- package/dist/harness/generation.d.ts.map +1 -1
- package/dist/harness/generation.js +67 -27
- package/dist/harness/generation.js.map +1 -1
- package/dist/harness/harness.d.ts +5 -2
- package/dist/harness/harness.d.ts.map +1 -1
- package/dist/harness/harness.js +78 -13
- package/dist/harness/harness.js.map +1 -1
- package/dist/harness/inbox.d.ts +2 -2
- package/dist/harness/inbox.d.ts.map +1 -1
- package/dist/harness/inbox.js +3 -3
- package/dist/harness/inbox.js.map +1 -1
- package/dist/harness/prompt.d.ts +1 -1
- package/dist/harness/prompt.d.ts.map +1 -1
- package/dist/harness/prompt.js +1 -1
- package/dist/harness/prompt.js.map +1 -1
- package/dist/harness/scheduler.d.ts +55 -10
- package/dist/harness/scheduler.d.ts.map +1 -1
- package/dist/harness/scheduler.js +617 -124
- package/dist/harness/scheduler.js.map +1 -1
- package/dist/harness/submissions.d.ts +7 -2
- package/dist/harness/submissions.d.ts.map +1 -1
- package/dist/harness/submissions.js +48 -17
- package/dist/harness/submissions.js.map +1 -1
- package/dist/harness/task-graph.d.ts +18 -1
- package/dist/harness/task-graph.d.ts.map +1 -1
- package/dist/harness/task-graph.js +50 -11
- package/dist/harness/task-graph.js.map +1 -1
- package/dist/harness/telemetry.d.ts +307 -0
- package/dist/harness/telemetry.d.ts.map +1 -0
- package/dist/harness/telemetry.js +136 -0
- package/dist/harness/telemetry.js.map +1 -0
- package/dist/harness/tool.d.ts.map +1 -1
- package/dist/harness/tool.js +54 -8
- package/dist/harness/tool.js.map +1 -1
- package/dist/harness/types.d.ts +110 -15
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/harness/util.d.ts +4 -0
- package/dist/harness/util.d.ts.map +1 -1
- package/dist/harness/util.js +4 -0
- package/dist/harness/util.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/session/session.d.ts +17 -6
- package/dist/session/session.d.ts.map +1 -1
- package/dist/session/session.js +64 -32
- package/dist/session/session.js.map +1 -1
- package/dist/session/transaction.d.ts +5 -1
- package/dist/session/transaction.d.ts.map +1 -1
- package/dist/session/transaction.js +26 -1
- package/dist/session/transaction.js.map +1 -1
- package/dist/storage/indexeddb.d.ts +60 -0
- package/dist/storage/indexeddb.d.ts.map +1 -0
- package/dist/storage/indexeddb.js +831 -0
- package/dist/storage/indexeddb.js.map +1 -0
- package/dist/storage/jsonl/storage.d.ts +16 -0
- package/dist/storage/jsonl/storage.d.ts.map +1 -1
- package/dist/storage/memory.js +1 -1
- package/dist/storage/memory.js.map +1 -1
- package/dist/storage/sqlite/cloudflare.d.ts +6 -43
- package/dist/storage/sqlite/cloudflare.d.ts.map +1 -1
- package/dist/storage/sqlite/cloudflare.js +6 -101
- package/dist/storage/sqlite/cloudflare.js.map +1 -1
- package/dist/storage/sqlite/database.d.ts +1 -36
- package/dist/storage/sqlite/database.d.ts.map +1 -1
- package/dist/storage/sqlite/database.js.map +1 -1
- package/dist/storage/sqlite/index.d.ts +2 -2
- package/dist/storage/sqlite/index.d.ts.map +1 -1
- package/dist/storage/sqlite/index.js.map +1 -1
- package/dist/storage/sqlite/migrations.d.ts +7 -6
- package/dist/storage/sqlite/migrations.d.ts.map +1 -1
- package/dist/storage/sqlite/migrations.js +116 -106
- package/dist/storage/sqlite/migrations.js.map +1 -1
- package/dist/storage/sqlite/node.d.ts +6 -39
- package/dist/storage/sqlite/node.d.ts.map +1 -1
- package/dist/storage/sqlite/node.js +8 -175
- package/dist/storage/sqlite/node.js.map +1 -1
- package/dist/storage/sqlite/schema.d.ts +45 -0
- package/dist/storage/sqlite/schema.d.ts.map +1 -0
- package/dist/storage/sqlite/schema.js +50 -0
- package/dist/storage/sqlite/schema.js.map +1 -0
- package/dist/storage/sqlite/storage.d.ts +10 -2
- package/dist/storage/sqlite/storage.d.ts.map +1 -1
- package/dist/storage/sqlite/storage.js +46 -43
- package/dist/storage/sqlite/storage.js.map +1 -1
- package/dist/testing/storage-conformance.d.ts.map +1 -1
- package/dist/testing/storage-conformance.js +17 -0
- package/dist/testing/storage-conformance.js.map +1 -1
- package/dist/timers.d.ts +21 -0
- package/dist/timers.d.ts.map +1 -0
- package/dist/timers.js +72 -0
- package/dist/timers.js.map +1 -0
- package/dist/tools/image-processor.d.ts +4 -1
- package/dist/tools/image-processor.d.ts.map +1 -1
- package/dist/tools/image-processor.js +8 -18
- package/dist/tools/image-processor.js.map +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +1 -1
- package/dist/tools/read.js.map +1 -1
- package/dist/truncate.d.ts +3 -2
- package/dist/truncate.d.ts.map +1 -1
- package/dist/truncate.js +4 -34
- package/dist/truncate.js.map +1 -1
- package/dist/types.d.ts +130 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +15 -5
- package/dist/harness/output.d.ts +0 -69
- package/dist/harness/output.d.ts.map +0 -1
- package/dist/harness/output.js +0 -318
- package/dist/harness/output.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/harness/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport type { AssistantMessage, Message, ToolCall, ToolResultMessage } from \"@earendil-works/pi-ai\";\nimport { idFromNumber } from \"../ids.ts\";\nimport type { SessionImpl } from \"../session/session.ts\";\nimport type { ContextEdit, ConversationId, Cursor, EntryId, EntryQuery, EntryRecord, Storage } from \"../types.ts\";\nimport type { ContextView } from \"./types.ts\";\n\nconst SCAN_PAGE_SIZE = 256;\nconst EXCLUDED_STOP_REASONS: ReadonlySet<AssistantMessage[\"stopReason\"]> = new Set([\"aborted\", \"error\", \"deferred\"]);\nconst MISSING_RESULT_TEXT = \"Tool result unavailable: history ends before this call completed.\";\n\n/** Head marker and newest visible entry that fix one committed context range. */\nexport type ContextBounds = {\n\treadonly head: (EntryRecord & { readonly head: EntryId }) | undefined;\n\treadonly tail: EntryId;\n};\n\n/**\n * Capture the bounds of the current context, or of the context cut off at the visible entry `at`, with two O(1)\n * reads. Run this on the Session line; entries at or below the tail are immutable, so `deriveContext()` can then scan\n * them off the line.\n */\nexport async function captureContextBounds(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat?: EntryId,\n): Promise<ContextBounds | undefined> {\n\tlet tail: EntryId | undefined;\n\tif (at === undefined) {\n\t\ttail = (await storage.scanEntries({ conversationId }, 1, undefined, context)).items[0]?.id;\n\t\tif (tail === undefined) return undefined;\n\t} else {\n\t\tif ((await storage.entry(conversationId, at, context)) === undefined) {\n\t\t\tthrow new Error(`Entry ${at} is not visible from conversation ${conversationId}`);\n\t\t}\n\t\ttail = at;\n\t}\n\treturn { head: await storage.findLatestHeadMarker(conversationId, tail, context), tail };\n}\n\n/**\n * One context read: the visible entries it scanned, from the head marker's head, or transcript start, through\n * `bounds.tail`, oldest first, and the view derived from them. Entries at or below the tail never change, so a later\n * read with the same head marker extends both.\n */\nexport type ContextRange = {\n\treadonly bounds: ContextBounds;\n\treadonly entries: readonly EntryRecord[];\n\treadonly view: ContextView;\n\t/** Targets of the edits in `entries`. */\n\treadonly edited: ReadonlySet<EntryId>;\n\t/**\n\t * `view.messages` from the contributed messages before the last assistant message. Tool results are ordered within\n\t * the messages up to the next assistant message, so later entries cannot change these.\n\t */\n\treadonly settled: readonly Message[];\n\t/** Contributed messages from the last assistant message on, before tool result ordering. */\n\treadonly open: readonly Message[];\n};\n\n/**\n * `readContext()` that reuses `previous`, an earlier range of the same conversation: with the same head marker, only\n * entries after its tail are scanned. Returns the view and the range to pass to the next read. A range outlives the\n * read, so its entries are frozen like `MemoryStorage` records, and the returned view has its own arrays.\n */\nexport async function readContextFrom(\n\tsession: SessionImpl,\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat: EntryId | undefined,\n\tprevious: ContextRange | undefined,\n): Promise<{ readonly view: ContextView; readonly range: ContextRange | undefined }> {\n\tconst bounds = await session.readOnLine(() => captureContextBounds(storage, conversationId, context, at));\n\tif (bounds === undefined) return { view: emptyView(), range: undefined };\n\tfreezeJson(bounds.head);\n\tlet range: ContextRange;\n\tif (previous === undefined || previous.bounds.head?.id !== bounds.head?.id) {\n\t\trange = deriveRange(bounds, freezeJson(await scanRange(storage, conversationId, rangeQuery(bounds), context)));\n\t} else if (bounds.tail === previous.bounds.tail) {\n\t\trange = previous;\n\t} else if (bounds.tail < previous.bounds.tail) {\n\t\trange = deriveRange(\n\t\t\tbounds,\n\t\t\tprevious.entries.filter((entry) => entry.id <= bounds.tail),\n\t\t);\n\t} else {\n\t\tconst minEntryId = idFromNumber<EntryId>(previous.bounds.tail + 1);\n\t\tconst added = await scanRange(storage, conversationId, { minEntryId, maxEntryId: bounds.tail }, context);\n\t\trange = extendRange(previous, bounds, freezeJson(added));\n\t}\n\tconst { view } = range;\n\treturn {\n\t\tview: {\n\t\t\t...view,\n\t\t\tentries: [...view.entries],\n\t\t\tcontributions: [...view.contributions],\n\t\t\tmessages: [...view.messages],\n\t\t},\n\t\trange,\n\t};\n}\n\n/** Committed context of one conversation: bounds captured on the Session line, entries derived off it. */\nexport async function readContext(\n\tsession: SessionImpl,\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat?: EntryId,\n): Promise<ContextView> {\n\tconst bounds = await session.readOnLine(() => captureContextBounds(storage, conversationId, context, at));\n\treturn deriveContext(storage, conversationId, bounds, context);\n}\n\n/**\n * Derive the active transcript and model context of one conversation within captured bounds.\n *\n * H = newest visible head marker; the range runs from `H.head` (or transcript start) through the tail. Per target,\n * the newest edit in the range wins. Context entries are H followed by the range's non-head entries.\n */\nexport async function deriveContext(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tbounds: ContextBounds | undefined,\n\tcontext: Context,\n): Promise<ContextView> {\n\tif (bounds === undefined) return emptyView();\n\treturn deriveRange(bounds, await scanRange(storage, conversationId, rangeQuery(bounds), context)).view;\n}\n\nfunction emptyView(): ContextView {\n\treturn { head: undefined, entries: [], contributions: [], messages: [] };\n}\n\n/** Derive the context view from the entries scanned within `bounds`. */\nfunction deriveRange(bounds: ContextBounds, entries: readonly EntryRecord[]): ContextRange {\n\tconst edits = new Map<EntryId, ContextEdit>();\n\t// Edits of every entry in the range count, including older head markers that `selectActive()` drops.\n\tfor (const entry of entries) for (const edit of entry.edits ?? []) edits.set(edit.target, edit);\n\tconst active = selectActive(bounds.head, entries);\n\tconst contributions = active.map((entry) => contribute(entry, edits.get(entry.id)));\n\tconst { settled, open } = settle([], contributions.flat());\n\tconst messages = leadWithSystem([...settled, ...orderToolResults(open)]);\n\tconst view = { head: bounds.head, entries: active, contributions, messages };\n\treturn { bounds, entries, view, edited: new Set(edits.keys()), settled, open };\n}\n\n/**\n * `previous` extended by the entries `added` after its tail under the same head marker: only their contributions and\n * the open messages are derived. An added edit can change an earlier entry, so it derives the whole range again.\n */\nfunction extendRange(previous: ContextRange, bounds: ContextBounds, added: readonly EntryRecord[]): ContextRange {\n\tconst entries = [...previous.entries, ...added];\n\tif (added.some((entry) => entry.edits !== undefined || entry.head !== undefined || previous.edited.has(entry.id))) {\n\t\treturn deriveRange(bounds, entries);\n\t}\n\tconst contributions = added.map((entry) => contribute(entry, undefined));\n\tconst { settled, open } = settle(previous.settled, [...previous.open, ...contributions.flat()]);\n\tconst view = {\n\t\thead: bounds.head,\n\t\tentries: [...previous.view.entries, ...added],\n\t\tcontributions: [...previous.view.contributions, ...contributions],\n\t\tmessages: leadWithSystem([...settled, ...orderToolResults(open)]),\n\t};\n\treturn { bounds, entries, view, edited: previous.edited, settled, open };\n}\n\n/**\n * Move a system message that only user messages precede to the front. A run's input is committed before generation\n * renders the system prompt, so a transcript, or the range after a compaction or reset, starts with user messages\n * followed by the baseline system message. Providers treat only a leading system message as the initial prompt and tool\n * set; without it, a later tool change rewrites the request's tool list and invalidates the whole prompt cache.\n */\nfunction leadWithSystem(messages: Message[]): Message[] {\n\tconst index = messages.findIndex((message) => message.role !== \"user\");\n\tif (index <= 0 || messages[index]!.role !== \"system\") return messages;\n\treturn [messages[index]!, ...messages.slice(0, index), ...messages.slice(index + 1)];\n}\n\n/** One active entry's model messages after its edit and excluded stop reasons, before tool result ordering. */\nfunction contribute(entry: EntryRecord, edit: ContextEdit | undefined): readonly Message[] {\n\tif (edit?.action === \"omit\") return Object.freeze([]);\n\tconst contributed = edit?.action === \"replace\" ? edit.messages : (entry.model ?? []);\n\treturn Object.freeze(\n\t\tcontributed.filter((message) => message.role !== \"assistant\" || !EXCLUDED_STOP_REASONS.has(message.stopReason)),\n\t);\n}\n\n/** Objects `freezeJson()` froze with everything in them. `Object.isFrozen()` holds for shallow freezes too. */\nconst deeplyFrozen = new WeakSet<object>();\n\n/** Freeze a JSON value and everything in it. */\nfunction freezeJson<T>(value: T): T {\n\tif (value === null || typeof value !== \"object\" || deeplyFrozen.has(value)) return value;\n\tfor (const child of Object.values(value)) freezeJson(child);\n\tObject.freeze(value);\n\tdeeplyFrozen.add(value);\n\treturn value;\n}\n\n/**\n * Move the ordered messages before the last assistant message of `open` to `settled`. `orderToolResults()` of a\n * sequence equals the concatenation over its parts when each later part starts with an assistant message.\n */\nfunction settle(\n\tsettled: readonly Message[],\n\topen: readonly Message[],\n): { readonly settled: readonly Message[]; readonly open: readonly Message[] } {\n\tconst last = open.findLastIndex((message) => message.role === \"assistant\");\n\tif (last <= 0) return { settled, open };\n\treturn { settled: [...settled, ...orderToolResults(open.slice(0, last))], open: open.slice(last) };\n}\n\n/** The raw active entries within captured bounds, without deriving model context. */\nexport async function activeEntries(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tbounds: ContextBounds | undefined,\n\tcontext: Context,\n): Promise<readonly EntryRecord[]> {\n\tif (bounds === undefined) return [];\n\treturn selectActive(bounds.head, await scanRange(storage, conversationId, rangeQuery(bounds), context));\n}\n\n/** The visible range of `bounds`: from the head marker's head, or transcript start, through the tail. */\nfunction rangeQuery(bounds: ContextBounds): Omit<EntryQuery, \"conversationId\"> {\n\tconst head = bounds.head;\n\treturn head === undefined ? { maxEntryId: bounds.tail } : { minEntryId: head.head, maxEntryId: bounds.tail };\n}\n\n/** Visible entries within `range`, oldest first. */\nasync function scanRange(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\trange: Omit<EntryQuery, \"conversationId\">,\n\tcontext: Context,\n): Promise<EntryRecord[]> {\n\tconst entries: EntryRecord[] = [];\n\tlet cursor: Cursor | undefined;\n\tdo {\n\t\tconst page = await storage.scanEntries({ conversationId, ...range }, SCAN_PAGE_SIZE, cursor, context);\n\t\tentries.push(...page.items);\n\t\tcursor = page.next;\n\t} while (cursor !== undefined);\n\treturn entries.reverse();\n}\n\n/** The head marker followed by the range's non-head entries, or the whole range without a marker. */\nfunction selectActive(head: ContextBounds[\"head\"], range: readonly EntryRecord[]): readonly EntryRecord[] {\n\treturn head === undefined ? range : [head, ...range.filter((entry) => entry.head === undefined)];\n}\n\n/**\n * Place each assistant's tool results directly after it in call order. Results are taken from the messages before\n * the next assistant; a missing result is synthesized and unmatched results are dropped.\n */\nexport function orderToolResults(messages: readonly Message[]): Message[] {\n\tconst ordered: Message[] = [];\n\tfor (let index = 0; index < messages.length; index++) {\n\t\tconst message = messages[index]!;\n\t\tif (message.role === \"toolResult\") continue;\n\t\tordered.push(message);\n\t\tif (message.role !== \"assistant\") continue;\n\t\tconst calls = message.content.filter((content): content is ToolCall => content.type === \"toolCall\");\n\t\tif (calls.length === 0) continue;\n\t\tconst results = new Map<string, number>();\n\t\tfor (let next = index + 1; next < messages.length && messages[next]!.role !== \"assistant\"; next++) {\n\t\t\tconst candidate = messages[next]!;\n\t\t\tif (candidate.role === \"toolResult\" && !results.has(candidate.toolCallId)) {\n\t\t\t\tresults.set(candidate.toolCallId, next);\n\t\t\t}\n\t\t}\n\t\tfor (const call of calls) {\n\t\t\tconst resultIndex = results.get(call.id);\n\t\t\tordered.push(resultIndex === undefined ? missingResult(call, message.timestamp) : messages[resultIndex]!);\n\t\t}\n\t}\n\treturn ordered;\n}\n\nfunction missingResult(call: ToolCall, timestamp: number): ToolResultMessage {\n\treturn freezeJson({\n\t\trole: \"toolResult\",\n\t\ttoolCallId: call.id,\n\t\ttoolName: call.name,\n\t\tcontent: [{ type: \"text\", text: MISSING_RESULT_TEXT }],\n\t\tisError: true,\n\t\tdetails: { reason: \"missing_result\" },\n\t\ttimestamp,\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/harness/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport type { AssistantMessage, Message, ToolCall, ToolResultMessage } from \"@earendil-works/pi-ai\";\nimport { SystemEntry } from \"../entries.ts\";\nimport { idFromNumber } from \"../ids.ts\";\nimport type { SessionImpl } from \"../session/session.ts\";\nimport type { ContextEdit, ConversationId, Cursor, EntryId, EntryQuery, EntryRecord, Storage } from \"../types.ts\";\nimport type { ContextView } from \"./types.ts\";\n\nconst SCAN_PAGE_SIZE = 256;\nconst EXCLUDED_STOP_REASONS: ReadonlySet<AssistantMessage[\"stopReason\"]> = new Set([\"aborted\", \"error\", \"deferred\"]);\nconst MISSING_RESULT_TEXT = \"Tool result unavailable: history ends before this call completed.\";\n\n/** Head marker and newest visible entry that fix one committed context range. */\nexport type ContextBounds = {\n\treadonly head: (EntryRecord & { readonly head: EntryId }) | undefined;\n\treadonly tail: EntryId;\n};\n\n/**\n * Capture the bounds of the current context, or of the context cut off at the visible entry `at`, with two O(1)\n * reads. Run this on the Session line; entries at or below the tail are immutable, so `deriveContext()` can then scan\n * them off the line.\n */\nexport async function captureContextBounds(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat?: EntryId,\n): Promise<ContextBounds | undefined> {\n\tlet tail: EntryId | undefined;\n\tif (at === undefined) {\n\t\ttail = (await storage.scanEntries({ conversationId }, 1, undefined, context)).items[0]?.id;\n\t\tif (tail === undefined) return undefined;\n\t} else {\n\t\tif ((await storage.entry(conversationId, at, context)) === undefined) {\n\t\t\tthrow new Error(`Entry ${at} is not visible from conversation ${conversationId}`);\n\t\t}\n\t\ttail = at;\n\t}\n\treturn { head: await storage.findLatestHeadMarker(conversationId, tail, context), tail };\n}\n\n/**\n * One context read: the visible entries it scanned, from the head marker's head, or transcript start, through\n * `bounds.tail`, oldest first, and the view derived from them. Entries at or below the tail never change, so a later\n * read with the same head marker extends both.\n */\nexport type ContextRange = {\n\treadonly bounds: ContextBounds;\n\treadonly entries: readonly EntryRecord[];\n\treadonly view: ContextView;\n\t/** Targets of the edits in `entries`. */\n\treadonly edited: ReadonlySet<EntryId>;\n\t/**\n\t * `view.messages` from the contributed messages before the last assistant message. Tool results are ordered within\n\t * the messages up to the next assistant message, so later entries cannot change these.\n\t */\n\treadonly settled: readonly Message[];\n\t/** Contributed messages from the last assistant message on, before tool result ordering. */\n\treadonly open: readonly Message[];\n\t/** The baseline's system message; see `findBaseline()`. */\n\treadonly baseline: Baseline;\n};\n\n/** A baseline's system message, `null` when the range cannot have one, or `undefined` while a later entry may be one. */\ntype Baseline = Message | null | undefined;\n\n/**\n * `readContext()` that reuses `previous`, an earlier range of the same conversation: with the same head marker, only\n * entries after its tail are scanned. Returns the view and the range to pass to the next read. A range outlives the\n * read, so its entries are frozen like `MemoryStorage` records, and the returned view has its own arrays.\n */\nexport async function readContextFrom(\n\tsession: SessionImpl,\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat: EntryId | undefined,\n\tprevious: ContextRange | undefined,\n): Promise<{ readonly view: ContextView; readonly range: ContextRange | undefined }> {\n\tconst bounds = await session.readOnLine(() => captureContextBounds(storage, conversationId, context, at));\n\tif (bounds === undefined) return { view: emptyView(), range: undefined };\n\tfreezeJson(bounds.head);\n\tlet range: ContextRange;\n\tif (previous === undefined || previous.bounds.head?.id !== bounds.head?.id) {\n\t\trange = deriveRange(bounds, freezeJson(await scanRange(storage, conversationId, rangeQuery(bounds), context)));\n\t} else if (bounds.tail === previous.bounds.tail) {\n\t\trange = previous;\n\t} else if (bounds.tail < previous.bounds.tail) {\n\t\trange = deriveRange(\n\t\t\tbounds,\n\t\t\tprevious.entries.filter((entry) => entry.id <= bounds.tail),\n\t\t);\n\t} else {\n\t\tconst minEntryId = idFromNumber<EntryId>(previous.bounds.tail + 1);\n\t\tconst added = await scanRange(storage, conversationId, { minEntryId, maxEntryId: bounds.tail }, context);\n\t\trange = extendRange(previous, bounds, freezeJson(added));\n\t}\n\tconst { view } = range;\n\treturn {\n\t\tview: {\n\t\t\t...view,\n\t\t\tentries: [...view.entries],\n\t\t\tcontributions: [...view.contributions],\n\t\t\tmessages: [...view.messages],\n\t\t},\n\t\trange,\n\t};\n}\n\n/** Committed context of one conversation: bounds captured on the Session line, entries derived off it. */\nexport async function readContext(\n\tsession: SessionImpl,\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat?: EntryId,\n): Promise<ContextView> {\n\tconst bounds = await session.readOnLine(() => captureContextBounds(storage, conversationId, context, at));\n\treturn deriveContext(storage, conversationId, bounds, context);\n}\n\n/**\n * Derive the active transcript and model context of one conversation within captured bounds.\n *\n * H = newest visible head marker; the range runs from `H.head` (or transcript start) through the tail. Per target,\n * the newest edit in the range wins. Context entries are H followed by the range's non-head entries.\n */\nexport async function deriveContext(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tbounds: ContextBounds | undefined,\n\tcontext: Context,\n): Promise<ContextView> {\n\tif (bounds === undefined) return emptyView();\n\treturn deriveRange(bounds, await scanRange(storage, conversationId, rangeQuery(bounds), context)).view;\n}\n\nfunction emptyView(): ContextView {\n\treturn { head: undefined, entries: [], contributions: [], messages: [] };\n}\n\n/** Derive the context view from the entries scanned within `bounds`. */\nfunction deriveRange(bounds: ContextBounds, entries: readonly EntryRecord[]): ContextRange {\n\tconst edits = new Map<EntryId, ContextEdit>();\n\t// Edits of every entry in the range count, including older head markers that `selectActive()` drops.\n\tfor (const entry of entries) for (const edit of entry.edits ?? []) edits.set(edit.target, edit);\n\tconst active = selectActive(bounds.head, entries);\n\tconst contributions = active.map((entry) => contribute(entry, edits.get(entry.id)));\n\tconst { settled, open } = settle([], contributions.flat());\n\tconst baseline = findBaseline(bounds.head, active);\n\tconst messages = leadWithSystem([...settled, ...orderToolResults(open)], baseline);\n\tconst view = { head: bounds.head, entries: active, contributions, messages };\n\treturn { bounds, entries, view, edited: new Set(edits.keys()), settled, open, baseline };\n}\n\n/**\n * `previous` extended by the entries `added` after its tail under the same head marker: only their contributions and\n * the open messages are derived. An added edit can change an earlier entry, so it derives the whole range again.\n */\nfunction extendRange(previous: ContextRange, bounds: ContextBounds, added: readonly EntryRecord[]): ContextRange {\n\tconst entries = [...previous.entries, ...added];\n\tif (added.some((entry) => entry.edits !== undefined || entry.head !== undefined || previous.edited.has(entry.id))) {\n\t\treturn deriveRange(bounds, entries);\n\t}\n\tconst contributions = added.map((entry) => contribute(entry, undefined));\n\tconst { settled, open } = settle(previous.settled, [...previous.open, ...contributions.flat()]);\n\tconst baseline = previous.baseline !== undefined ? previous.baseline : findBaseline(bounds.head, added);\n\tconst view = {\n\t\thead: bounds.head,\n\t\tentries: [...previous.view.entries, ...added],\n\t\tcontributions: [...previous.view.contributions, ...contributions],\n\t\tmessages: leadWithSystem([...settled, ...orderToolResults(open)], baseline),\n\t};\n\treturn { bounds, entries, view, edited: previous.edited, settled, open, baseline };\n}\n\n/**\n * The baseline among `entries`, oldest first: the system message of the first `pi.system` entry after the head\n * marker, or of the transcript without one, when generation wrote it with every section and tool in effect. After a\n * head marker it always does, and omits the retained `pi.system` entries. Without one it does when it saw no system\n * message before, which holds unless an earlier entry carries one, even if an edit omits it later. An edit that targets\n * the baseline contributes other messages, or none, so none leads; a later entry does not take its place.\n */\nfunction findBaseline(head: ContextBounds[\"head\"], entries: readonly EntryRecord[]): Baseline {\n\tfor (const entry of entries) {\n\t\tif (head !== undefined && entry.id <= head.id) continue;\n\t\tif (SystemEntry.is(entry)) return entry.model?.find((message) => message.role === \"system\") ?? null;\n\t\tif (head === undefined && carriesSystemMessage(entry)) return null;\n\t}\n\treturn undefined;\n}\n\n/** Whether an entry's own messages, or the replacement messages of its edits, include a system message. */\nfunction carriesSystemMessage(entry: EntryRecord): boolean {\n\tconst isSystem = (message: Message) => message.role === \"system\";\n\treturn (\n\t\t(entry.model?.some(isSystem) ?? false) ||\n\t\t(entry.edits?.some((edit) => edit.action === \"replace\" && edit.messages.some(isSystem)) ?? false)\n\t);\n}\n\n/**\n * Move the first system message to the front when only user messages precede it, or when it is the baseline's. A run's\n * input is committed before generation renders the system prompt, so a transcript starts with the input followed by\n * the baseline, and the range after a compaction with the kept history, assistant messages included. Providers treat\n * only a leading system message as the initial prompt and tool set; without it, a later tool change rewrites the\n * request's tool list and invalidates the whole prompt cache. Only the first system message moves, so replaying\n * system messages in order gives the same sections and tools. Any other system message, before an assistant message,\n * stays where it was committed: moving it would change what it means to the model.\n */\nfunction leadWithSystem(messages: Message[], baseline: Baseline): Message[] {\n\tconst index = messages.findIndex((message) => message.role === \"system\");\n\tif (index <= 0) return messages;\n\tconst system = messages[index]!;\n\tif (system !== baseline && messages.slice(0, index).some((message) => message.role !== \"user\")) {\n\t\treturn messages;\n\t}\n\treturn [system, ...messages.slice(0, index), ...messages.slice(index + 1)];\n}\n\n/** One active entry's model messages after its edit and excluded stop reasons, before tool result ordering. */\nfunction contribute(entry: EntryRecord, edit: ContextEdit | undefined): readonly Message[] {\n\tif (edit?.action === \"omit\") return Object.freeze([]);\n\tconst contributed = edit?.action === \"replace\" ? edit.messages : (entry.model ?? []);\n\treturn Object.freeze(\n\t\tcontributed.filter((message) => message.role !== \"assistant\" || !EXCLUDED_STOP_REASONS.has(message.stopReason)),\n\t);\n}\n\n/** Objects `freezeJson()` froze with everything in them. `Object.isFrozen()` holds for shallow freezes too. */\nconst deeplyFrozen = new WeakSet<object>();\n\n/** Freeze a JSON value and everything in it. */\nfunction freezeJson<T>(value: T): T {\n\tif (value === null || typeof value !== \"object\" || deeplyFrozen.has(value)) return value;\n\tfor (const child of Object.values(value)) freezeJson(child);\n\tObject.freeze(value);\n\tdeeplyFrozen.add(value);\n\treturn value;\n}\n\n/**\n * Move the ordered messages before the last assistant message of `open` to `settled`. `orderToolResults()` of a\n * sequence equals the concatenation over its parts when each later part starts with an assistant message.\n */\nfunction settle(\n\tsettled: readonly Message[],\n\topen: readonly Message[],\n): { readonly settled: readonly Message[]; readonly open: readonly Message[] } {\n\tconst last = open.findLastIndex((message) => message.role === \"assistant\");\n\tif (last <= 0) return { settled, open };\n\treturn { settled: [...settled, ...orderToolResults(open.slice(0, last))], open: open.slice(last) };\n}\n\n/** The raw active entries within captured bounds, without deriving model context. */\nexport async function activeEntries(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tbounds: ContextBounds | undefined,\n\tcontext: Context,\n): Promise<readonly EntryRecord[]> {\n\tif (bounds === undefined) return [];\n\treturn selectActive(bounds.head, await scanRange(storage, conversationId, rangeQuery(bounds), context));\n}\n\n/** The visible range of `bounds`: from the head marker's head, or transcript start, through the tail. */\nfunction rangeQuery(bounds: ContextBounds): Omit<EntryQuery, \"conversationId\"> {\n\tconst head = bounds.head;\n\treturn head === undefined ? { maxEntryId: bounds.tail } : { minEntryId: head.head, maxEntryId: bounds.tail };\n}\n\n/** Visible entries within `range`, oldest first. */\nasync function scanRange(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\trange: Omit<EntryQuery, \"conversationId\">,\n\tcontext: Context,\n): Promise<EntryRecord[]> {\n\tconst entries: EntryRecord[] = [];\n\tlet cursor: Cursor | undefined;\n\tdo {\n\t\tconst page = await storage.scanEntries({ conversationId, ...range }, SCAN_PAGE_SIZE, cursor, context);\n\t\tentries.push(...page.items);\n\t\tcursor = page.next;\n\t} while (cursor !== undefined);\n\treturn entries.reverse();\n}\n\n/** The head marker followed by the range's non-head entries, or the whole range without a marker. */\nfunction selectActive(head: ContextBounds[\"head\"], range: readonly EntryRecord[]): readonly EntryRecord[] {\n\treturn head === undefined ? range : [head, ...range.filter((entry) => entry.head === undefined)];\n}\n\n/**\n * Place each assistant's tool results directly after it in call order. Results are taken from the messages before\n * the next assistant; a missing result is synthesized and unmatched results are dropped.\n */\nexport function orderToolResults(messages: readonly Message[]): Message[] {\n\tconst ordered: Message[] = [];\n\tfor (let index = 0; index < messages.length; index++) {\n\t\tconst message = messages[index]!;\n\t\tif (message.role === \"toolResult\") continue;\n\t\tordered.push(message);\n\t\tif (message.role !== \"assistant\") continue;\n\t\tconst calls = message.content.filter((content): content is ToolCall => content.type === \"toolCall\");\n\t\tif (calls.length === 0) continue;\n\t\tconst results = new Map<string, number>();\n\t\tfor (let next = index + 1; next < messages.length && messages[next]!.role !== \"assistant\"; next++) {\n\t\t\tconst candidate = messages[next]!;\n\t\t\tif (candidate.role === \"toolResult\" && !results.has(candidate.toolCallId)) {\n\t\t\t\tresults.set(candidate.toolCallId, next);\n\t\t\t}\n\t\t}\n\t\tfor (const call of calls) {\n\t\t\tconst resultIndex = results.get(call.id);\n\t\t\tordered.push(resultIndex === undefined ? missingResult(call, message.timestamp) : messages[resultIndex]!);\n\t\t}\n\t}\n\treturn ordered;\n}\n\nfunction missingResult(call: ToolCall, timestamp: number): ToolResultMessage {\n\treturn freezeJson({\n\t\trole: \"toolResult\",\n\t\ttoolCallId: call.id,\n\t\ttoolName: call.name,\n\t\tcontent: [{ type: \"text\", text: MISSING_RESULT_TEXT }],\n\t\tisError: true,\n\t\tdetails: { reason: \"missing_result\" },\n\t\ttimestamp,\n\t});\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generation.d.ts","sourceRoot":"","sources":["../../src/harness/generation.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,KAAK,EAAkB,MAAM,uBAAuB,CAAC;AAC3F,OAAO,KAAK,EAGX,cAAc,EAGd,kBAAkB,EAGlB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"generation.d.ts","sourceRoot":"","sources":["../../src/harness/generation.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,KAAK,EAAkB,MAAM,uBAAuB,CAAC;AAC3F,OAAO,KAAK,EAGX,cAAc,EAGd,kBAAkB,EAGlB,MAAM,uBAAuB,CAAC;AAQ/B,OAAO,KAAK,EACX,cAAc,EACd,OAAO,EAEP,YAAY,EAEZ,MAAM,EAGN,EAAE,EAEF,MAAM,aAAa,CAAC;AAKrB,OAAO,EAAmB,KAAK,SAAS,EAAiB,MAAM,WAAW,CAAC;AAG3E,OAAO,EAA4C,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAC1F,OAAO,KAAK,EAGX,gBAAgB,EAEhB,yBAAyB,EACzB,eAAe,EACf,QAAQ,EAIR,MAAM,YAAY,CAAC;AAIpB,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAEpD,MAAM,MAAM,oBAAoB,GAC7B;IACA,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,qGAAqG;IACrG,SAAS,CAAC,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACrC,gGAAgG;IAChG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+GAA+G;IAC/G,SAAS,CAAC,EAAE,IAAI,CAAC;CAChB,GACD;IACA,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACrC,KAAK,EAAE,QAAQ,CAAC;IAChB,aAAa,EAAE,kBAAkB,CAAC;IAClC,6GAA6G;IAC7G,aAAa,EAAE,yBAAyB,CAAC;IACzC,4CAA4C;IAC5C,MAAM,EAAE,OAAO,CAAC;CACf,GACD;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACxF;IACA,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACrC,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CACd,GACD;IACA,gFAAgF;IAChF,KAAK,EAAE,OAAO,CAAC;IACf,+BAA+B;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,qGAAqG;IACrG,KAAK,EAAE,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC;IAChC,kEAAkE;IAClE,OAAO,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAEL,MAAM,MAAM,gBAAgB,GAAG;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAmBpD;;;GAGG;AACH,eAAO,MAAM,cAAc,sGAkMzB,CAAC;AAkHH;;;GAGG;AACH,wBAAsB,cAAc,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAKlH;AAyTD,gGAAgG;AAChG,wBAAsB,QAAQ,CAC7B,EAAE,EAAE,EAAE,EACN,cAAc,EAAE,cAAc,EAC9B,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,EACtB,MAAM,EAAE,YAAY,EAAE,GACpB,OAAO,CAAC,IAAI,CAAC,CAEf;AAWD,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAE/E"}
|
|
@@ -4,15 +4,16 @@ import { isRetryableAssistantError, retryDelayMs } from "@earendil-works/pi-ai/u
|
|
|
4
4
|
import { getCurrentTools } from "@earendil-works/pi-ai/utils/transcript";
|
|
5
5
|
import { AssistantEntry, ResetEntry, SystemEntry, UserEntry } from "../entries.js";
|
|
6
6
|
import { defineTask } from "../tasks.js";
|
|
7
|
-
import { addTools } from "./agent.js";
|
|
8
|
-
import { createCompaction, estimateContext, selectCut } from "./compaction.js";
|
|
9
|
-
import { applyBoundary, prepareBoundary } from "./inbox.js";
|
|
7
|
+
import { addTools, sectionExtension } from "./agent.js";
|
|
8
|
+
import { compactionPolicy, createCompaction, estimateContext, selectCut } from "./compaction.js";
|
|
9
|
+
import { applyBoundary, prepareBoundary, withdrawQueuedInputs } from "./inbox.js";
|
|
10
10
|
import { assignJson } from "./json.js";
|
|
11
11
|
import { endRun, LiveDoc } from "./live.js";
|
|
12
12
|
import { planSystemEntries, renderSections, replaySections } from "./prompt.js";
|
|
13
13
|
import { ensureProviderSessionId } from "./provider.js";
|
|
14
14
|
import { appendToolResult, harnessError, ToolTask } from "./tool.js";
|
|
15
15
|
import { recordUsage } from "./usage.js";
|
|
16
|
+
import { optionalExtension } from "./util.js";
|
|
16
17
|
const DEFAULT_POLL_AFTER_MS = 5000;
|
|
17
18
|
/**
|
|
18
19
|
* Built-in generation task: prepares the positional system prompt and tool loadout, requests or polls the model,
|
|
@@ -54,7 +55,6 @@ export const GenerationTask = defineTask({
|
|
|
54
55
|
}
|
|
55
56
|
const view = await runtime.context(conversationId, context);
|
|
56
57
|
const shown = replaySections(view.messages);
|
|
57
|
-
const report = (error) => runtime.report(error);
|
|
58
58
|
let env;
|
|
59
59
|
try {
|
|
60
60
|
env = await runtime.env(context);
|
|
@@ -62,13 +62,16 @@ export const GenerationTask = defineTask({
|
|
|
62
62
|
catch (error) {
|
|
63
63
|
if (context.abortSignal?.aborted)
|
|
64
64
|
throw error;
|
|
65
|
-
report(error);
|
|
65
|
+
runtime.report(error, { type: "host", callback: "env" });
|
|
66
66
|
}
|
|
67
67
|
const input = { conversationId, agent, env, shown: Object.fromEntries(shown), read: runtime };
|
|
68
|
-
const desired = await renderSections(agent.sections, input, shown, report,
|
|
68
|
+
const desired = await renderSections(agent.sections, input, shown, (error, section) => runtime.report(error, {
|
|
69
|
+
type: "extension",
|
|
70
|
+
...optionalExtension(sectionExtension(agent, section.key)),
|
|
71
|
+
}), context);
|
|
69
72
|
const entries = planSystemEntries(view, desired, agent.tools, runtime.now());
|
|
70
73
|
const threshold = compacted === undefined
|
|
71
|
-
? thresholdCompaction(view, entries, resolved.contextWindow, settings
|
|
74
|
+
? thresholdCompaction(view, entries, resolved.contextWindow, compactionPolicy(settings, model))
|
|
72
75
|
: undefined;
|
|
73
76
|
if (threshold === "blocking") {
|
|
74
77
|
// Compact first and prepare again; the transcript is unchanged until the compaction appends.
|
|
@@ -122,6 +125,7 @@ export const GenerationTask = defineTask({
|
|
|
122
125
|
const options = {
|
|
123
126
|
...streamOptions,
|
|
124
127
|
signal: runtime.signal,
|
|
128
|
+
telemetry: context.telemetry,
|
|
125
129
|
sessionId: await ensureProviderSessionId(runtime, context),
|
|
126
130
|
...(thinkingLevel === "off" ? {} : { reasoning: thinkingLevel }),
|
|
127
131
|
};
|
|
@@ -129,9 +133,11 @@ export const GenerationTask = defineTask({
|
|
|
129
133
|
const request = { attempt, compacted, model: ref, cutoff, messages: view.messages };
|
|
130
134
|
await classify(runtime, request, message, context);
|
|
131
135
|
},
|
|
136
|
+
/** After the backoff's sleep; a run before `until` (an early wake, or a record of an earlier version) sleeps again. */
|
|
132
137
|
retry: async (task, runtime, context) => {
|
|
133
138
|
const { attempt, compacted, until } = task.state.checkpoint;
|
|
134
|
-
|
|
139
|
+
if (until > runtime.now())
|
|
140
|
+
return runtime.sleepUntil(until, task.state.checkpoint, context);
|
|
135
141
|
await runtime.commit(async (tx) => {
|
|
136
142
|
(await tx.doc(LiveDoc, runtime.conversationId)).generation = { attempt: attempt + 1 };
|
|
137
143
|
const checkpoint = {
|
|
@@ -142,13 +148,18 @@ export const GenerationTask = defineTask({
|
|
|
142
148
|
return { status: "running", checkpoint };
|
|
143
149
|
}, context);
|
|
144
150
|
},
|
|
151
|
+
/** After the sleep until `pollAt`; a run before it (an early wake, or a record of an earlier version) sleeps again. */
|
|
145
152
|
poll: async (task, runtime, context) => {
|
|
146
153
|
const { attempt, compacted, model: ref, cutoff, handle, pollAt } = task.state.checkpoint;
|
|
154
|
+
if (pollAt > runtime.now())
|
|
155
|
+
return runtime.sleepUntil(pollAt, task.state.checkpoint, context);
|
|
147
156
|
const model = runtime.models.getModel(ref.provider, ref.modelId);
|
|
148
157
|
if (model === undefined)
|
|
149
158
|
return failNoModel(runtime, ref, context);
|
|
150
|
-
await runtime.
|
|
151
|
-
|
|
159
|
+
const message = await runtime.models.fetchDeferred(model, handle, {
|
|
160
|
+
signal: runtime.signal,
|
|
161
|
+
telemetry: context.telemetry,
|
|
162
|
+
});
|
|
152
163
|
const request = { attempt, compacted, model: ref, cutoff, pollAt };
|
|
153
164
|
await classify(runtime, request, message, context);
|
|
154
165
|
},
|
|
@@ -180,10 +191,13 @@ export const GenerationTask = defineTask({
|
|
|
180
191
|
const model = runtime.models.getModel(checkpoint.model.provider, checkpoint.model.modelId);
|
|
181
192
|
if (model !== undefined) {
|
|
182
193
|
try {
|
|
183
|
-
await runtime.models.cancelDeferred(model, checkpoint.handle, {
|
|
194
|
+
await runtime.models.cancelDeferred(model, checkpoint.handle, {
|
|
195
|
+
signal: runtime.signal,
|
|
196
|
+
telemetry: context.telemetry,
|
|
197
|
+
});
|
|
184
198
|
}
|
|
185
199
|
catch (error) {
|
|
186
|
-
runtime.report(error);
|
|
200
|
+
runtime.report(error, { type: "host", callback: "models" });
|
|
187
201
|
}
|
|
188
202
|
}
|
|
189
203
|
}
|
|
@@ -245,8 +259,9 @@ function compactionFailure(outcome) {
|
|
|
245
259
|
/** Settle the run's inputs `unanswered` with `model_error` and fail with `text`. */
|
|
246
260
|
async function failModelError(runtime, text, context) {
|
|
247
261
|
await runtime.commit(async (tx) => {
|
|
262
|
+
const failure = await prepareFailure(tx, runtime);
|
|
248
263
|
const live = await tx.doc(LiveDoc, runtime.conversationId);
|
|
249
|
-
|
|
264
|
+
await endFailedRun(tx, runtime, live, failure, { status: "unanswered", reason: "model_error", detail: text });
|
|
250
265
|
return {
|
|
251
266
|
status: "terminal",
|
|
252
267
|
outcome: { status: "failed", error: { message: text, detail: { reason: "model_error" } } },
|
|
@@ -257,11 +272,34 @@ async function failModelError(runtime, text, context) {
|
|
|
257
272
|
async function failNoModel(runtime, ref, context) {
|
|
258
273
|
const message = ref === undefined ? "No model is configured" : `Model ${ref.provider}/${ref.modelId} is not available`;
|
|
259
274
|
await runtime.commit(async (tx) => {
|
|
275
|
+
const failure = await prepareFailure(tx, runtime);
|
|
260
276
|
const live = await tx.doc(LiveDoc, runtime.conversationId);
|
|
261
|
-
|
|
277
|
+
await endFailedRun(tx, runtime, live, failure, { status: "unanswered", reason: "no_model" });
|
|
262
278
|
return { status: "terminal", outcome: { status: "failed", error: { message, detail: { reason: "no_model" } } } };
|
|
263
279
|
}, context);
|
|
264
280
|
}
|
|
281
|
+
async function prepareFailure(tx, runtime) {
|
|
282
|
+
const settings = runtime.settings;
|
|
283
|
+
const policy = settings.afterFailedRun;
|
|
284
|
+
if (policy !== "continue")
|
|
285
|
+
return { policy };
|
|
286
|
+
return { policy, boundary: await prepareBoundary(tx, runtime.conversationId, settings) };
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* End a failed run, settling its inputs with `settlement`, and do with the inputs queued behind it what
|
|
290
|
+
* `settings.afterFailedRun` says (spec §6): keep them, start the next run with them, or withdraw them.
|
|
291
|
+
*/
|
|
292
|
+
async function endFailedRun(tx, runtime, live, failure, settlement) {
|
|
293
|
+
const conversationId = runtime.conversationId;
|
|
294
|
+
endRun(tx, live, runtime.taskId, settlement);
|
|
295
|
+
if (failure.policy === "withdraw")
|
|
296
|
+
await withdrawQueuedInputs(tx, conversationId, "run_failed");
|
|
297
|
+
if (failure.boundary === undefined)
|
|
298
|
+
return;
|
|
299
|
+
const { users } = await applyBoundary(tx, failure.boundary, "final", runtime.now());
|
|
300
|
+
if (users.length > 0)
|
|
301
|
+
await startRun(tx, conversationId, live, users);
|
|
302
|
+
}
|
|
265
303
|
/**
|
|
266
304
|
* Append a committed partial left by an interrupted, aborted, faulted, or orphaned attempt as an aborted assistant
|
|
267
305
|
* entry; the caller replaces or removes `generation`.
|
|
@@ -308,7 +346,7 @@ async function streamResponse(runtime, model, messages, options, attempt, contex
|
|
|
308
346
|
.finally(() => {
|
|
309
347
|
inFlight = undefined;
|
|
310
348
|
if (pending !== undefined && !stopped)
|
|
311
|
-
timer =
|
|
349
|
+
timer = runtime.setTimer(flush, interval);
|
|
312
350
|
});
|
|
313
351
|
};
|
|
314
352
|
try {
|
|
@@ -320,13 +358,13 @@ async function streamResponse(runtime, model, messages, options, attempt, contex
|
|
|
320
358
|
continue;
|
|
321
359
|
pending = event.partial;
|
|
322
360
|
if (timer === undefined && inFlight === undefined)
|
|
323
|
-
timer =
|
|
361
|
+
timer = runtime.setTimer(flush, interval);
|
|
324
362
|
}
|
|
325
363
|
return await events.result();
|
|
326
364
|
}
|
|
327
365
|
finally {
|
|
328
366
|
stopped = true;
|
|
329
|
-
|
|
367
|
+
timer?.();
|
|
330
368
|
await inFlight;
|
|
331
369
|
}
|
|
332
370
|
}
|
|
@@ -350,7 +388,7 @@ async function classify(runtime, request, message, context) {
|
|
|
350
388
|
handle,
|
|
351
389
|
pollAt,
|
|
352
390
|
};
|
|
353
|
-
return { status: "
|
|
391
|
+
return { status: "sleeping", checkpoint, until: pollAt };
|
|
354
392
|
}, context);
|
|
355
393
|
return;
|
|
356
394
|
}
|
|
@@ -365,10 +403,10 @@ async function classify(runtime, request, message, context) {
|
|
|
365
403
|
// The retry and compaction policies govern the next attempt, so they are read now rather than pinned at preparation.
|
|
366
404
|
const settings = runtime.settings;
|
|
367
405
|
const overflow = message.stopReason === "error" && isContextOverflow(message);
|
|
368
|
-
|
|
369
|
-
|
|
406
|
+
const compaction = compactionPolicy(settings, ref);
|
|
407
|
+
if (overflow && compacted === undefined && compaction.enabled) {
|
|
370
408
|
const view = await runtime.context(conversationId, context, { at: cutoff });
|
|
371
|
-
if (selectCut(view,
|
|
409
|
+
if (selectCut(view, compaction.keepRecentTokens) !== undefined) {
|
|
372
410
|
const text = message.errorMessage ?? "Context overflow";
|
|
373
411
|
await runtime.commit(async (tx) => {
|
|
374
412
|
const live = await tx.doc(LiveDoc, conversationId);
|
|
@@ -390,9 +428,11 @@ async function classify(runtime, request, message, context) {
|
|
|
390
428
|
attempt <= policy.maxRetries;
|
|
391
429
|
const until = retry ? runtime.now() + retryDelayMs(policy, attempt) : 0;
|
|
392
430
|
await runtime.commit(async (tx) => {
|
|
431
|
+
// Read before the first table write; only a failure uses it.
|
|
432
|
+
const failure = retry ? undefined : await prepareFailure(tx, runtime);
|
|
393
433
|
const live = await tx.doc(LiveDoc, conversationId);
|
|
394
434
|
await appendAssistant(tx, conversationId, message);
|
|
395
|
-
if (
|
|
435
|
+
if (failure === undefined) {
|
|
396
436
|
live.generation = { attempt, retry: { at: until, error: message.errorMessage ?? "" } };
|
|
397
437
|
const checkpoint = {
|
|
398
438
|
phase: "retry",
|
|
@@ -400,10 +440,10 @@ async function classify(runtime, request, message, context) {
|
|
|
400
440
|
...(compacted === undefined ? {} : { compacted }),
|
|
401
441
|
until,
|
|
402
442
|
};
|
|
403
|
-
return { status: "
|
|
443
|
+
return { status: "sleeping", checkpoint, until };
|
|
404
444
|
}
|
|
405
445
|
const text = message.errorMessage ?? `Model response ended with stop reason ${message.stopReason}`;
|
|
406
|
-
|
|
446
|
+
await endFailedRun(tx, runtime, live, failure, { status: "unanswered", reason: "model_error", detail: text });
|
|
407
447
|
return {
|
|
408
448
|
status: "terminal",
|
|
409
449
|
outcome: { status: "failed", error: { message: text, detail: { reason: "model_error" } } },
|
|
@@ -552,11 +592,11 @@ async function appendAssistant(tx, conversationId, message) {
|
|
|
552
592
|
}
|
|
553
593
|
/** Start a run for `inputs`, placed input submissions: a new generation takes `pi.live.run`. */
|
|
554
594
|
export async function startRun(tx, conversationId, live, inputs) {
|
|
555
|
-
live.run = { taskId: await createGeneration(tx, conversationId), inputs };
|
|
595
|
+
live.run = { taskId: await createGeneration(tx, conversationId, { submissionIds: inputs }), inputs };
|
|
556
596
|
}
|
|
557
597
|
/** A generation owned by its conversation. */
|
|
558
|
-
function createGeneration(tx, conversationId) {
|
|
559
|
-
return tx.createTask(GenerationTask, {}, { ownership: { kind: "conversation" }, conversationId });
|
|
598
|
+
function createGeneration(tx, conversationId, telemetry = {}) {
|
|
599
|
+
return tx.createTask(GenerationTask, {}, { ownership: { kind: "conversation" }, conversationId, ...telemetry });
|
|
560
600
|
}
|
|
561
601
|
/** Hand run control from `from` to `to`; the run's inputs move with it. */
|
|
562
602
|
export function handOver(live, from, to) {
|