@indexnetwork/protocol 4.4.1 → 4.5.0-rc.322.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/dist/chat/chat.interrupt.classifier.d.ts.map +1 -1
- package/dist/chat/chat.interrupt.classifier.js +2 -2
- package/dist/chat/chat.interrupt.classifier.js.map +1 -1
- package/dist/chat/chat.suggester.d.ts.map +1 -1
- package/dist/chat/chat.suggester.js +2 -3
- package/dist/chat/chat.suggester.js.map +1 -1
- package/dist/chat/chat.summarizer.d.ts.map +1 -1
- package/dist/chat/chat.summarizer.js +11 -3
- package/dist/chat/chat.summarizer.js.map +1 -1
- package/dist/chat/chat.title.generator.d.ts.map +1 -1
- package/dist/chat/chat.title.generator.js +2 -2
- package/dist/chat/chat.title.generator.js.map +1 -1
- package/dist/contact/contact.inviter.d.ts.map +1 -1
- package/dist/contact/contact.inviter.js +2 -3
- package/dist/contact/contact.inviter.js.map +1 -1
- package/dist/context/context.generator.d.ts.map +1 -1
- package/dist/context/context.generator.js +2 -2
- package/dist/context/context.generator.js.map +1 -1
- package/dist/enrichment/enrichment.generator.d.ts +1 -1
- package/dist/enrichment/enrichment.generator.d.ts.map +1 -1
- package/dist/enrichment/enrichment.generator.js +3 -4
- package/dist/enrichment/enrichment.generator.js.map +1 -1
- package/dist/intent/intent.clarifier.d.ts.map +1 -1
- package/dist/intent/intent.clarifier.js +4 -5
- package/dist/intent/intent.clarifier.js.map +1 -1
- package/dist/intent/intent.indexer.d.ts.map +1 -1
- package/dist/intent/intent.indexer.js +2 -3
- package/dist/intent/intent.indexer.js.map +1 -1
- package/dist/intent/intent.inferrer.d.ts.map +1 -1
- package/dist/intent/intent.inferrer.js +2 -3
- package/dist/intent/intent.inferrer.js.map +1 -1
- package/dist/intent/intent.reconciler.d.ts.map +1 -1
- package/dist/intent/intent.reconciler.js +2 -3
- package/dist/intent/intent.reconciler.js.map +1 -1
- package/dist/intent/intent.verifier.d.ts.map +1 -1
- package/dist/intent/intent.verifier.js +2 -3
- package/dist/intent/intent.verifier.js.map +1 -1
- package/dist/negotiation/insight.generator.d.ts.map +1 -1
- package/dist/negotiation/insight.generator.js +2 -2
- package/dist/negotiation/insight.generator.js.map +1 -1
- package/dist/negotiation/negotiation.agent.js +2 -2
- package/dist/negotiation/negotiation.agent.js.map +1 -1
- package/dist/negotiation/negotiation.summarizer.d.ts +11 -0
- package/dist/negotiation/negotiation.summarizer.d.ts.map +1 -1
- package/dist/negotiation/negotiation.summarizer.js +13 -3
- package/dist/negotiation/negotiation.summarizer.js.map +1 -1
- package/dist/network/network.recommender.d.ts.map +1 -1
- package/dist/network/network.recommender.js +2 -3
- package/dist/network/network.recommender.js.map +1 -1
- package/dist/opportunity/feed/feed.categorizer.d.ts.map +1 -1
- package/dist/opportunity/feed/feed.categorizer.js +2 -3
- package/dist/opportunity/feed/feed.categorizer.js.map +1 -1
- package/dist/opportunity/opportunity.evaluator.d.ts.map +1 -1
- package/dist/opportunity/opportunity.evaluator.js +3 -4
- package/dist/opportunity/opportunity.evaluator.js.map +1 -1
- package/dist/opportunity/opportunity.presenter.d.ts.map +1 -1
- package/dist/opportunity/opportunity.presenter.js +3 -4
- package/dist/opportunity/opportunity.presenter.js.map +1 -1
- package/dist/opportunity/question.generator.d.ts.map +1 -1
- package/dist/opportunity/question.generator.js +17 -3
- package/dist/opportunity/question.generator.js.map +1 -1
- package/dist/premise/premise.analyzer.d.ts.map +1 -1
- package/dist/premise/premise.analyzer.js +2 -3
- package/dist/premise/premise.analyzer.js.map +1 -1
- package/dist/premise/premise.decomposer.d.ts.map +1 -1
- package/dist/premise/premise.decomposer.js +2 -3
- package/dist/premise/premise.decomposer.js.map +1 -1
- package/dist/premise/premise.indexer.d.ts.map +1 -1
- package/dist/premise/premise.indexer.js +2 -3
- package/dist/premise/premise.indexer.js.map +1 -1
- package/dist/questioner/questioner.agent.d.ts +2 -2
- package/dist/questioner/questioner.agent.d.ts.map +1 -1
- package/dist/questioner/questioner.agent.js +12 -4
- package/dist/questioner/questioner.agent.js.map +1 -1
- package/dist/shared/agent/model.config.d.ts +41 -2
- package/dist/shared/agent/model.config.d.ts.map +1 -1
- package/dist/shared/agent/model.config.js +98 -1
- package/dist/shared/agent/model.config.js.map +1 -1
- package/dist/shared/hyde/hyde.generator.d.ts.map +1 -1
- package/dist/shared/hyde/hyde.generator.js +2 -3
- package/dist/shared/hyde/hyde.generator.js.map +1 -1
- package/dist/shared/hyde/lens.inferrer.d.ts.map +1 -1
- package/dist/shared/hyde/lens.inferrer.js +2 -3
- package/dist/shared/hyde/lens.inferrer.js.map +1 -1
- package/package.json +1 -1
|
@@ -7,9 +7,24 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
|
+
/**
|
|
11
|
+
* @deprecated Use QuestionerAgent instead. Will be removed in a future version.
|
|
12
|
+
*
|
|
13
|
+
* QuestionGenerator — pure LLM pass that turns a structured DiscoveryQuestionInput
|
|
14
|
+
* into 0–3 decision questions. No DB, no events, no caller wired here; Slice 3
|
|
15
|
+
* (opportunity.discover.ts) is the first consumer.
|
|
16
|
+
*
|
|
17
|
+
* Flow:
|
|
18
|
+
* 1. buildQuestionPrompt(input) → user message string.
|
|
19
|
+
* 2. model.invoke([system, user]) returns a structured payload.
|
|
20
|
+
* 3. safeParse via QuestionGeneratorResponseSchema → null on failure.
|
|
21
|
+
* 4. Guardrails: dedup by title, then strategy-diversity (never 3 same).
|
|
22
|
+
* 5. If empty, return null. Otherwise split into public Question[] + parallel
|
|
23
|
+
* QuestionStrategy[] (debug-only; strategy is NEVER on the public shape).
|
|
24
|
+
*/
|
|
10
25
|
import { HumanMessage, SystemMessage } from "@langchain/core/messages";
|
|
11
26
|
import { QuestionGeneratorResponseSchema } from "../shared/schemas/question.schema.js";
|
|
12
|
-
import {
|
|
27
|
+
import { createStructuredModel } from "../shared/agent/model.config.js";
|
|
13
28
|
import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
|
|
14
29
|
import { protocolLogger } from "../shared/observability/protocol.logger.js";
|
|
15
30
|
import { Timed } from "../shared/observability/performance.js";
|
|
@@ -20,8 +35,7 @@ const MAX_SAME_STRATEGY = 2;
|
|
|
20
35
|
/** @deprecated Use QuestionerAgent instead. Will be removed in a future version. */
|
|
21
36
|
export class QuestionGenerator {
|
|
22
37
|
constructor() {
|
|
23
|
-
|
|
24
|
-
this.model = llm.withStructuredOutput(QuestionGeneratorResponseSchema, {
|
|
38
|
+
this.model = createStructuredModel("discoveryQuestionGenerator", QuestionGeneratorResponseSchema, {
|
|
25
39
|
name: "clarifying_questions",
|
|
26
40
|
});
|
|
27
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"question.generator.js","sourceRoot":"/","sources":["opportunity/question.generator.ts"],"names":[],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"question.generator.js","sourceRoot":"/","sources":["opportunity/question.generator.ts"],"names":[],"mappings":";;;;;;;;;AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEvE,OAAO,EAAE,+BAA+B,EAAkG,MAAM,sCAAsC,CAAC;AACvL,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AAC5E,OAAO,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAA+B,MAAM,sBAAsB,CAAC;AAEvG,MAAM,MAAM,GAAG,cAAc,CAAC,mBAAmB,CAAC,CAAC;AAEnD,oEAAoE;AACpE,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAE5B,oFAAoF;AACpF,MAAM,OAAO,iBAAiB;IAG5B;QACE,IAAI,CAAC,KAAK,GAAG,qBAAqB,CAAC,4BAA4B,EAAE,+BAA+B,EAAE;YAChG,IAAI,EAAE,sBAAsB;SAC7B,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;;;OAUG;IAEG,AAAN,KAAK,CAAC,QAAQ,CACZ,KAA6B,EAC7B,OAAkC;QAElC,MAAM,IAAI,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAExC,IAAI,GAAY,CAAC;QACjB,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,qBAAqB,CAC/B,IAAI,CAAC,KAAK,EACV,CAAC,IAAI,aAAa,CAAC,aAAa,CAAC,EAAE,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,EAC1D,OAAO,EAAE,MAAM,CAChB,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,sEAAsE;YACtE,sEAAsE;YACtE,sCAAsC;YACtC,MAAM,OAAO,GAAG,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,KAAK,CAAC;YAClD,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,CAAC,IAAI,CAAC,qCAAqC,EAAE;oBACjD,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,SAAS,CAAC;iBAChI,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC,mCAAmC,EAAE;oBAC/C,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;iBACxD,CAAC,CAAC;YACL,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,MAAM,GAAG,+BAA+B,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAC9D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,CAAC,IAAI,CAAC,gCAAgC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC/E,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAEvC,OAAO;YACL,SAAS,EAAE,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC;YACtC,UAAU,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;SAC5C,CAAC;IACJ,CAAC;CACF;AA5CO;IADL,KAAK,EAAE;;;;iDA4CP;AAGH;;;;;;;;GAQG;AACH,SAAS,eAAe,CAAC,SAAiC;IACxD,MAAM,cAAc,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;IAC/C,OAAO,wBAAwB,CAAC,cAAc,CAAC,CAAC;AAClD,CAAC;AAED,SAAS,YAAY,CAAC,SAAiC;IACrD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;YAAE,SAAS;QAChC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAClB,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACd,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,SAAS,wBAAwB,CAC/B,SAAiC;IAEjC,MAAM,MAAM,GAAG,IAAI,GAAG,EAA4B,CAAC;IACnD,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,CAAC,IAAI,iBAAiB;YAAE,SAAS;QACrC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9B,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACd,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,aAAa,CAAC,CAAuB;IAC5C,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,WAAW,EAAE,GAAG,CAAC,CAAC;IAClD,OAAO,WAAW,CAAC;AACrB,CAAC","sourcesContent":["/**\n * @deprecated Use QuestionerAgent instead. Will be removed in a future version.\n *\n * QuestionGenerator — pure LLM pass that turns a structured DiscoveryQuestionInput\n * into 0–3 decision questions. No DB, no events, no caller wired here; Slice 3\n * (opportunity.discover.ts) is the first consumer.\n *\n * Flow:\n * 1. buildQuestionPrompt(input) → user message string.\n * 2. model.invoke([system, user]) returns a structured payload.\n * 3. safeParse via QuestionGeneratorResponseSchema → null on failure.\n * 4. Guardrails: dedup by title, then strategy-diversity (never 3 same).\n * 5. If empty, return null. Otherwise split into public Question[] + parallel\n * QuestionStrategy[] (debug-only; strategy is NEVER on the public shape).\n */\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\n\nimport { QuestionGeneratorResponseSchema, type Question, type QuestionGenerationResult, type QuestionStrategy, type QuestionWithStrategy } from \"../shared/schemas/question.schema.js\";\nimport { createStructuredModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { SYSTEM_PROMPT, buildQuestionPrompt, type DiscoveryQuestionInput } from \"./question.prompt.js\";\n\nconst logger = protocolLogger(\"QuestionGenerator\");\n\n/** Maximum same-strategy questions allowed in a single emission. */\nconst MAX_SAME_STRATEGY = 2;\n\n/** @deprecated Use QuestionerAgent instead. Will be removed in a future version. */\nexport class QuestionGenerator {\n private model: ReturnType<typeof createStructuredModel>;\n\n constructor() {\n this.model = createStructuredModel(\"discoveryQuestionGenerator\", QuestionGeneratorResponseSchema, {\n name: \"clarifying_questions\",\n });\n }\n\n /**\n * Generate up to 3 decision questions from the given discovery turn.\n *\n * @param input Discovery turn payload.\n * @param options.signal Optional AbortSignal. Passed to LangChain so an\n * aborted call cancels the underlying HTTP fetch instead of leaving a\n * floating promise burning OpenRouter capacity.\n * @returns A result with parallel questions[] and strategies[] arrays,\n * or null when the LLM fails, the output is malformed, the call was\n * aborted, or the guardrails leave zero questions standing.\n */\n @Timed()\n async generate(\n input: DiscoveryQuestionInput,\n options?: { signal?: AbortSignal },\n ): Promise<QuestionGenerationResult | null> {\n const user = buildQuestionPrompt(input);\n\n let raw: unknown;\n try {\n raw = await invokeWithAbortSignal(\n this.model,\n [new SystemMessage(SYSTEM_PROMPT), new HumanMessage(user)],\n options?.signal,\n );\n } catch (err) {\n // AbortError is the expected outcome under deadline pressure — caller\n // already wants to skip questions in that case, so log at info rather\n // than warn to keep dashboards clean.\n const aborted = options?.signal?.aborted ?? false;\n if (aborted) {\n logger.info(\"QuestionGenerator aborted by signal\", {\n reason: options?.signal?.reason instanceof Error ? options.signal.reason.message : String(options?.signal?.reason ?? \"unknown\"),\n });\n } else {\n logger.warn(\"QuestionGenerator LLM call failed\", {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n return null;\n }\n\n const parsed = QuestionGeneratorResponseSchema.safeParse(raw);\n if (!parsed.success) {\n logger.warn(\"QuestionGenerator parse failed\", { error: parsed.error.message });\n return null;\n }\n\n const filtered = applyGuardrails(parsed.data.questions);\n if (filtered.length === 0) return null;\n\n return {\n questions: filtered.map(stripStrategy),\n strategies: filtered.map((q) => q.strategy),\n };\n }\n}\n\n/**\n * Guardrail pipeline. Order matters:\n * 1. Dedup by title (keep first occurrence). Title uniqueness is a hard UX\n * requirement for the renderer — two questions cannot share a chip label.\n * 2. Strategy diversity: cap same-strategy entries at MAX_SAME_STRATEGY,\n * dropping overflow unconditionally (does NOT depend on whether a\n * distinct-strategy alternative exists in the batch). The cap exists to\n * enforce the \"never 3 of the same kind\" guidance from the spec.\n */\nfunction applyGuardrails(questions: QuestionWithStrategy[]): QuestionWithStrategy[] {\n const dedupedByTitle = dedupByTitle(questions);\n return enforceStrategyDiversity(dedupedByTitle);\n}\n\nfunction dedupByTitle(questions: QuestionWithStrategy[]): QuestionWithStrategy[] {\n const seen = new Set<string>();\n const out: QuestionWithStrategy[] = [];\n for (const q of questions) {\n if (seen.has(q.title)) continue;\n seen.add(q.title);\n out.push(q);\n }\n return out;\n}\n\n/**\n * Enforce the \"never 3 same-strategy\" rule. Walks the array in order; once a\n * given strategy has appeared MAX_SAME_STRATEGY times, subsequent entries with\n * the same strategy are dropped. Distinct-strategy entries are always kept\n * (subject to the schema's overall 3-question cap, which has already applied).\n */\nfunction enforceStrategyDiversity(\n questions: QuestionWithStrategy[],\n): QuestionWithStrategy[] {\n const counts = new Map<QuestionStrategy, number>();\n const out: QuestionWithStrategy[] = [];\n for (const q of questions) {\n const n = counts.get(q.strategy) ?? 0;\n if (n >= MAX_SAME_STRATEGY) continue;\n counts.set(q.strategy, n + 1);\n out.push(q);\n }\n return out;\n}\n\nfunction stripStrategy(q: QuestionWithStrategy): Question {\n const { strategy: _strategy, ...publicShape } = q;\n return publicShape;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"premise.analyzer.d.ts","sourceRoot":"/","sources":["premise/premise.analyzer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgExB,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;EAoBlB,CAAC;AAEH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEnE;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,KAAK,
|
|
1
|
+
{"version":3,"file":"premise.analyzer.d.ts","sourceRoot":"/","sources":["premise/premise.analyzer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgExB,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;EAoBlB,CAAC;AAEH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEnE;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,KAAK,CAA2C;;IAS3C,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAwBlG"}
|
|
@@ -11,7 +11,7 @@ import { HumanMessage, SystemMessage } from "@langchain/core/messages";
|
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
import { protocolLogger } from "../shared/observability/protocol.logger.js";
|
|
13
13
|
import { Timed } from "../shared/observability/performance.js";
|
|
14
|
-
import {
|
|
14
|
+
import { createStructuredModel } from "../shared/agent/model.config.js";
|
|
15
15
|
import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
|
|
16
16
|
const logger = protocolLogger("PremiseAnalyzer");
|
|
17
17
|
const systemPrompt = `
|
|
@@ -83,8 +83,7 @@ const responseFormat = z.object({
|
|
|
83
83
|
*/
|
|
84
84
|
export class PremiseAnalyzer {
|
|
85
85
|
constructor() {
|
|
86
|
-
|
|
87
|
-
this.model = model.withStructuredOutput(responseFormat, {
|
|
86
|
+
this.model = createStructuredModel("premiseAnalyzer", responseFormat, {
|
|
88
87
|
name: "premise_analyzer"
|
|
89
88
|
});
|
|
90
89
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"premise.analyzer.js","sourceRoot":"/","sources":["premise/premise.analyzer.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AAC5E,OAAO,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAC/D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"premise.analyzer.js","sourceRoot":"/","sources":["premise/premise.analyzer.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AAC5E,OAAO,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAExE,MAAM,MAAM,GAAG,cAAc,CAAC,iBAAiB,CAAC,CAAC;AAEjD,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsDpB,CAAC;AAEF,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAC5B,+EAA+E;QAC/E,oCAAoC,CACrC;IACD,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC,QAAQ,CAC1D,6FAA6F,CAC9F;IACD,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CACpD,8EAA8E,CAC/E;IACD,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CACpD,8DAA8D,CAC/D;IACD,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAClD,+DAA+D,CAChE;IACD,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAChD,8EAA8E,CAC/E;CACF,CAAC,CAAC;AAIH;;GAEG;AACH,MAAM,OAAO,eAAe;IAG1B;QACE,IAAI,CAAC,KAAK,GAAG,qBAAqB,CAAC,iBAAiB,EAAE,cAAc,EAAE;YACpE,IAAI,EAAE,kBAAkB;SACzB,CAAC,CAAC;IACL,CAAC;IAGY,AAAN,KAAK,CAAC,MAAM,CAAC,WAAmB,EAAE,cAAuB;QAC9D,MAAM,CAAC,OAAO,CAAC,wCAAwC,WAAW,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;QAE3F,MAAM,YAAY,GAAG,cAAc;YACjC,CAAC,CAAC,kCAAkC,cAAc,IAAI;YACtD,CAAC,CAAC,EAAE,CAAC;QAEP,MAAM,MAAM,GAAG,GAAG,YAAY;;GAE/B,WAAW;;yDAE2C,CAAC;QAEtD,MAAM,QAAQ,GAAG;YACf,IAAI,aAAa,CAAC,YAAY,CAAC;YAC/B,IAAI,YAAY,CAAC,MAAM,CAAC;SACzB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACjE,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAE5C,MAAM,CAAC,OAAO,CAAC,oCAAoC,MAAM,CAAC,aAAa,YAAY,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;QAC7G,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AAxBc;IADZ,KAAK,EAAE;;;;6CAwBP","sourcesContent":["import { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { z } from \"zod\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { createStructuredModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\nconst logger = protocolLogger(\"PremiseAnalyzer\");\n\nconst systemPrompt = `\nYou are the Premise Analyzer for the Index Network — an intent-driven discovery protocol.\n\nYour job: classify a self-descriptive premise using adapted Speech Act Theory, then score its felicity conditions.\n\nA premise is a proposition a person asserts about themselves. It is NOT a desire or request (those are intents). Premises are conditions of possibility — facts about who someone is that ground opportunity discovery.\n\nAlways reason before classifying. Output reasoning first.\n\n═══════════════════════════════════════════════════\nSTEP 1 — CLASSIFY THE SPEECH ACT\n═══════════════════════════════════════════════════\n\nDECLARATIVE: The premise constitutes a fact about the speaker's identity, role, or status.\n Examples:\n · \"I am a climate-tech founder\" → DECLARATIVE\n · \"I hold a PhD in computational biology\" → DECLARATIVE\n · \"I am based in Berlin\" → DECLARATIVE\n · \"I am raising Series A\" → DECLARATIVE (constitutes current status)\n\nASSERTIVE: The premise describes a capability, experience, or characteristic.\n Examples:\n · \"I have 10 years of experience in distributed systems\" → ASSERTIVE\n · \"I built a collaboration platform used by 50k users\" → ASSERTIVE\n · \"I speak fluent Mandarin and German\" → ASSERTIVE\n · \"I specialize in zero-knowledge proofs\" → ASSERTIVE\n\n═══════════════════════════════════════════════════\nSTEP 2 — SCORE THE FELICITY CONDITIONS (0–100)\n═══════════════════════════════════════════════════\n\nAUTHORITY (Preparatory Condition)\n Does the speaker plausibly have standing to assert this?\n 100 → Highly specific, verifiable claim (\"I founded X in 2019\")\n 60 → Plausible but unverifiable (\"I have deep expertise in AI\")\n 20 → Implausible or grandiose (\"I am the world's leading expert\")\n\nSINCERITY (Sincerity Condition)\n Does the linguistic form suggest genuine self-description vs. aspiration?\n 100 → Present tense, first person, specific (\"I am a YC-backed founder\")\n 60 → Hedged or aspirational (\"I'm sort of getting into crypto\")\n 20 → Clearly aspirational masquerading as fact (\"I'm basically a VC\")\n\nCLARITY (Essential Condition)\n How specific and matchable is this premise?\n 100 → \"I build distributed database systems in Rust at a Series B startup\"\n 60 → \"I work in tech\" (clear direction, vague spec)\n 20 → \"I do things\" (barely informative)\n\nSEMANTIC ENTROPY → 0.0 to 1.0\n 0.0 = maximally constrained (role + domain + location + stage all specified)\n 1.0 = no constraints at all\n 0.0 example: \"I am a senior ML engineer at Google Brain in Mountain View\"\n 1.0 example: \"I'm a person\"\n`;\n\nconst responseFormat = z.object({\n reasoning: z.string().describe(\n \"Step-by-step analysis: (1) whether this is DECLARATIVE or ASSERTIVE and why, \" +\n \"(2) felicity condition assessment.\"\n ),\n speechActType: z.enum([\"DECLARATIVE\", \"ASSERTIVE\"]).describe(\n \"DECLARATIVE = constitutes identity/role/status; ASSERTIVE = describes capability/experience\"\n ),\n felicityAuthority: z.number().min(0).max(100).describe(\n \"Preparatory: does the speaker plausibly have standing to assert this (0-100)\"\n ),\n felicitySincerity: z.number().min(0).max(100).describe(\n \"Sincerity: genuine self-description vs. aspirational (0-100)\"\n ),\n felicityClarity: z.number().min(0).max(100).describe(\n \"Essential: how specific and matchable is this premise (0-100)\"\n ),\n semanticEntropy: z.number().min(0).max(1).describe(\n \"Constraint density: 0.0 = maximally specific, 1.0 = completely unconstrained\"\n ),\n});\n\nexport type PremiseAnalyzerOutput = z.infer<typeof responseFormat>;\n\n/**\n * Classifies a premise using adapted Speech Act Theory and scores felicity conditions.\n */\nexport class PremiseAnalyzer {\n private model: ReturnType<typeof createStructuredModel>;\n\n constructor() {\n this.model = createStructuredModel(\"premiseAnalyzer\", responseFormat, {\n name: \"premise_analyzer\"\n });\n }\n\n @Timed()\n public async invoke(premiseText: string, profileContext?: string): Promise<PremiseAnalyzerOutput> {\n logger.verbose(`[PremiseAnalyzer.invoke] Analyzing: \"${premiseText.substring(0, 50)}...\"`);\n\n const contextBlock = profileContext\n ? `\\n# Speaker Profile (Context)\\n${profileContext}\\n`\n : \"\";\n\n const prompt = `${contextBlock}\n# Premise to Analyze\n\"${premiseText}\"\n\nClassify this premise and score its felicity conditions.`;\n\n const messages = [\n new SystemMessage(systemPrompt),\n new HumanMessage(prompt),\n ];\n\n const result = await invokeWithAbortSignal(this.model, messages);\n const output = responseFormat.parse(result);\n\n logger.verbose(`[PremiseAnalyzer.invoke] Result: ${output.speechActType} entropy=${output.semanticEntropy}`);\n return output;\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"premise.decomposer.d.ts","sourceRoot":"/","sources":["premise/premise.decomposer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAkIxB,QAAA,MAAM,iBAAiB;;;;;;;;;;;;EAQrB,CAAC;AAEH,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAalB,CAAC;AAEH,oFAAoF;AACpF,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AACrE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE;;;GAGG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,KAAK,
|
|
1
|
+
{"version":3,"file":"premise.decomposer.d.ts","sourceRoot":"/","sources":["premise/premise.decomposer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAkIxB,QAAA,MAAM,iBAAiB;;;;;;;;;;;;EAQrB,CAAC;AAEH,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAalB,CAAC;AAEH,oFAAoF;AACpF,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AACrE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE;;;GAGG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,KAAK,CAA2C;;IAS3C,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,kBAAkB,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC;CAiDnI"}
|
|
@@ -11,7 +11,7 @@ import { HumanMessage, SystemMessage } from "@langchain/core/messages";
|
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
import { protocolLogger } from "../shared/observability/protocol.logger.js";
|
|
13
13
|
import { Timed } from "../shared/observability/performance.js";
|
|
14
|
-
import {
|
|
14
|
+
import { createStructuredModel } from "../shared/agent/model.config.js";
|
|
15
15
|
import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
|
|
16
16
|
const logger = protocolLogger("PremiseDecomposer");
|
|
17
17
|
const systemPrompt = `
|
|
@@ -152,8 +152,7 @@ const responseFormat = z.object({
|
|
|
152
152
|
*/
|
|
153
153
|
export class PremiseDecomposer {
|
|
154
154
|
constructor() {
|
|
155
|
-
|
|
156
|
-
this.model = model.withStructuredOutput(responseFormat, {
|
|
155
|
+
this.model = createStructuredModel("premiseDecomposer", responseFormat, {
|
|
157
156
|
name: "premise_decomposer",
|
|
158
157
|
});
|
|
159
158
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"premise.decomposer.js","sourceRoot":"/","sources":["premise/premise.decomposer.ts"],"names":[],"mappings":"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","sourcesContent":["import { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { z } from \"zod\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { createModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\nconst logger = protocolLogger(\"PremiseDecomposer\");\n\nconst systemPrompt = `\nYou are the Premise Decomposer for the Index Network — an intent-driven discovery protocol.\n\nYour job: decompose free-text input about a person into individual, atomic premises.\n\nA premise is a single self-descriptive proposition — a fact someone asserts about themselves.\nEach premise should be:\n1. **Atomic** — one fact per premise, not compound statements\n2. **First-person** — phrased as \"I am...\", \"I work at...\", \"I specialize in...\"\n3. **Self-descriptive** — about the person's identity, role, skills, interests, location, or context\n4. **Non-redundant** — do not repeat the same information in multiple premises\n\n═══════════════════════════════════════════════════\nINPUT TYPES\n═══════════════════════════════════════════════════\n\nYou may receive:\n- **Chat messages**: \"I'm a software engineer at Google, based in SF. I'm interested in AI safety.\"\n- **Bio text**: A paragraph describing someone's background\n- **Scraped content**: LinkedIn/GitHub/Twitter data about someone\n- **Mixed input**: Name, location, and free-text combined\n\n═══════════════════════════════════════════════════\nDECOMPOSITION RULES\n═══════════════════════════════════════════════════\n\n1. Extract every distinct factual claim about the person\n2. Convert third-person (\"She works at...\") to first-person (\"I work at...\")\n3. Separate compound statements: \"I know Python and Rust\" → two premises\n4. Capture the FULL breadth of self-description, not just job/title. In particular,\n do not drop:\n - **Skills & expertise** (\"I specialize in distributed systems\", \"I know Rust\")\n - **Interests & focus areas** (\"I am interested in AI safety\", \"I care about climate\")\n - **Background/narrative context** (education, prior roles, domains, affiliations)\n These map to what a profile would store as interests/skills/context — every such\n fact must surface as its own premise so nothing is lost.\n5. Classify each premise:\n - \\`assertive\\` — stable identity facts (role, skills, interests, education, location)\n - \\`contextual\\` — temporal/situational (current projects, events, fundraising status)\n6. For \\`contextual\\` premises, estimate \\`validityDays\\` — how many days the fact is\n likely to remain true from now (e.g. \"speaking next week\" → ~7, \"raising a Series A\"\n → ~120, \"currently building X\" → ~180). Use null for \\`assertive\\` premises, which\n have no natural expiry.\n7. Skip vague or uninformative statements (\"I like stuff\", \"I'm a person\")\n8. Skip desires, requests, or intents (\"I'm looking for...\", \"I want to...\") — those are intents, not premises\n9. If the input contains NO extractable premises (e.g. just \"Yes\" or \"Hello\"), return an empty array\n10. NEVER extract denials or removal instructions as premises. \"I have nothing to do with X\",\n \"I am not a Y\", or \"remove X from my profile\" are retractions, not facts — handle them\n via the retraction rules below and do NOT emit a premise for them.\n\n═════════════════════════════════════════════════\nRETRACTION RULES\n═════════════════════════════════════════════════\n\nWhen the message includes the user's EXISTING PREMISES (listed with ids), you must also\ndetect which of them the input disavows and return their ids in \\`retractedPremiseIds\\`.\n\nA premise must be retracted when the input:\n- Explicitly asks to remove it (\"remove all mentions of X\", \"delete the part about Y\")\n- Denies it (\"I have nothing to do with X\", \"I never worked at Y\", \"that's not true\")\n- States it no longer holds (\"I no longer live in Berlin\", \"I left Google\")\n- Directly contradicts it (\"I'm based in Istanbul\" contradicts \"I am based in Ankara\")\n\nRules:\n- Only return ids that appear in the provided EXISTING PREMISES list — never invent ids.\n- Retract EVERY existing premise that matches the disavowed topic, not just the first one.\n- Do not retract premises the input merely omits — silence is not disavowal.\n- If no existing premises are provided, or nothing is disavowed, return an empty array.\n- Retraction and extraction are independent: an input can retract old premises AND\n contribute new ones in the same pass.\n\n═══════════════════════════════════════════════════\nBIO REVISION RULES\n═══════════════════════════════════════════════════\n\nWhen the message includes the user's CURRENT BIO, check whether the input disavows,\nremoves, or corrects anything that appears in it.\n\n- If it does, return \\`revisedBio\\`: the bio rewritten with those facts removed or\n corrected. Preserve everything the input does not dispute — same tone, similar\n length, no inventions.\n- If the input explicitly rewrites the bio (\"update my bio to X\"), return that\n rewritten bio.\n- NEVER add new facts to the bio — new information becomes premises, not bio edits.\n Purely additive input (\"I also enjoy X\") must return revisedBio: null.\n- If the bio is unaffected, or no CURRENT BIO was provided, return null.\n- Never return a revised bio that still mentions a disavowed fact.\n\n═══════════════════════════════════════════════════\nEXAMPLES\n═══════════════════════════════════════════════════\n\nInput: \"I'm a climate tech founder based in Berlin. I have a PhD in renewable energy systems and I'm currently raising Series A.\"\nOutput:\n 1. \"I am a climate tech founder\" (assertive, validityDays: null)\n 2. \"I am based in Berlin\" (assertive, validityDays: null)\n 3. \"I hold a PhD in renewable energy systems\" (assertive, validityDays: null)\n 4. \"I am currently raising Series A\" (contextual, validityDays: 120)\n\nInput: \"John Doe is a senior ML engineer at Meta in Menlo Park. He has 8 years of experience in NLP and computer vision.\"\nOutput:\n 1. \"I am a senior ML engineer\" (assertive, validityDays: null)\n 2. \"I work at Meta\" (assertive, validityDays: null)\n 3. \"I am based in Menlo Park\" (assertive, validityDays: null)\n 4. \"I have 8 years of experience in NLP\" (assertive, validityDays: null)\n 5. \"I have experience in computer vision\" (assertive, validityDays: null)\n\nInput: \"Yes, create my profile\"\nOutput: [] (empty — no premises)\n\nInput: \"Remove all mentions of the HOPE programming language. I have nothing to do with it. I specialize in compiler design.\"\nExisting premises:\n - id: aaaa-1 · \"I am the creator of the HOPE programming language\"\n - id: aaaa-2 · \"I specialize in compiler design\"\n - id: aaaa-3 · \"I am based in Istanbul\"\nCurrent bio: \"Software engineer in Istanbul. Creator of the HOPE programming language. Specializes in compiler design.\"\nOutput:\n premises: [] (compiler design already exists as aaaa-2 — nothing new to add)\n retractedPremiseIds: [\"aaaa-1\"]\n revisedBio: \"Software engineer in Istanbul. Specializes in compiler design.\"\n`;\n\nconst premiseItemSchema = z.object({\n text: z.string().describe(\"The premise text in first person (e.g. 'I am a software engineer at Google')\"),\n tier: z.enum([\"assertive\", \"contextual\"]).describe(\n \"assertive = stable identity fact; contextual = temporal/situational\"\n ),\n validityDays: z.number().int().positive().nullable().describe(\n \"For contextual premises: estimated days the fact remains true from now (its validity window). null for assertive premises, which do not expire.\"\n ),\n});\n\nconst responseFormat = z.object({\n reasoning: z.string().describe(\n \"Brief analysis of what factual claims can be extracted from the input\"\n ),\n premises: z.array(premiseItemSchema).describe(\n \"Array of extracted premises. Empty if input contains no self-descriptive facts.\"\n ),\n retractedPremiseIds: z.array(z.string()).default([]).describe(\n \"Ids of EXISTING premises (from the provided list) that the input disavows, denies, or asks to remove. Empty when nothing is disavowed or no existing premises were provided.\"\n ),\n revisedBio: z.string().nullable().default(null).describe(\n \"When the input disavows or corrects facts that appear in the provided CURRENT BIO, the bio rewritten without those facts (preserving everything else). null when the bio is unaffected or no bio was provided.\"\n ),\n});\n\n/** An existing ACTIVE premise offered to the decomposer for retraction matching. */\nexport interface ExistingPremiseRef {\n id: string;\n text: string;\n}\n\nexport type PremiseDecomposerOutput = z.infer<typeof responseFormat>;\nexport type DecomposedPremise = z.infer<typeof premiseItemSchema>;\n\n/**\n * Decomposes free-text input (chat messages, bios, scraped content) into\n * individual atomic premises suitable for the premise graph.\n */\nexport class PremiseDecomposer {\n private model: ReturnType<ReturnType<typeof createModel>[\"withStructuredOutput\"]>;\n\n constructor() {\n const model = createModel(\"premiseDecomposer\");\n this.model = model.withStructuredOutput(responseFormat, {\n name: \"premise_decomposer\",\n });\n }\n\n @Timed()\n public async invoke(input: string, existingPremises?: ExistingPremiseRef[], currentBio?: string): Promise<PremiseDecomposerOutput> {\n logger.verbose(`[PremiseDecomposer.invoke] Decomposing input (${input.length} chars, ${existingPremises?.length ?? 0} existing premise(s), bio: ${currentBio ? 'yes' : 'no'})`);\n\n const existingBlock = existingPremises?.length\n ? `\\n\\nEXISTING PREMISES (retract by id when the input disavows them):\\n${existingPremises\n .map((p) => `- id: ${p.id} · \"${p.text}\"`)\n .join(\"\\n\")}`\n : \"\";\n\n const bioBlock = currentBio?.trim()\n ? `\\n\\nCURRENT BIO (return revisedBio when the input disavows or corrects anything in it):\\n${currentBio.trim()}`\n : \"\";\n\n const prompt = `Decompose the following text into individual premises:\\n\\n${input}${existingBlock}${bioBlock}`;\n\n const messages = [\n new SystemMessage(systemPrompt),\n new HumanMessage(prompt),\n ];\n\n const result = await invokeWithAbortSignal(this.model, messages);\n const output = responseFormat.parse(result);\n\n // Guard against hallucinated ids: only keep retractions that reference premises we offered.\n const knownIds = new Set((existingPremises ?? []).map((p) => p.id));\n const droppedIds = output.retractedPremiseIds.filter((id) => !knownIds.has(id));\n if (droppedIds.length > 0) {\n logger.warn(`[PremiseDecomposer.invoke] Dropped ${droppedIds.length} unknown retraction id(s)`, { droppedIds });\n }\n output.retractedPremiseIds = output.retractedPremiseIds.filter((id) => knownIds.has(id));\n\n // A revised bio is only meaningful when we offered one to revise. Also\n // normalize structured-output quirks: literal \"null\", empty strings, and\n // no-op rewrites all mean \"bio unaffected\".\n const normalizedBio = output.revisedBio?.trim();\n if (\n !currentBio?.trim() ||\n !normalizedBio ||\n normalizedBio.toLowerCase() === 'null' ||\n normalizedBio === currentBio.trim()\n ) {\n output.revisedBio = null;\n } else {\n output.revisedBio = normalizedBio;\n }\n\n logger.verbose(`[PremiseDecomposer.invoke] Extracted ${output.premises.length} premise(s), ${output.retractedPremiseIds.length} retraction(s), revisedBio: ${output.revisedBio ? 'yes' : 'no'}`);\n return output;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"premise.decomposer.js","sourceRoot":"/","sources":["premise/premise.decomposer.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AAC5E,OAAO,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAExE,MAAM,MAAM,GAAG,cAAc,CAAC,mBAAmB,CAAC,CAAC;AAEnD,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwHpB,CAAC;AAEF,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8EAA8E,CAAC;IACzG,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC,QAAQ,CAChD,qEAAqE,CACtE;IACD,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAC3D,iJAAiJ,CAClJ;CACF,CAAC,CAAC;AAEH,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAC5B,uEAAuE,CACxE;IACD,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,QAAQ,CAC3C,iFAAiF,CAClF;IACD,mBAAmB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAC3D,8KAA8K,CAC/K;IACD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,CACtD,gNAAgN,CACjN;CACF,CAAC,CAAC;AAWH;;;GAGG;AACH,MAAM,OAAO,iBAAiB;IAG5B;QACE,IAAI,CAAC,KAAK,GAAG,qBAAqB,CAAC,mBAAmB,EAAE,cAAc,EAAE;YACtE,IAAI,EAAE,oBAAoB;SAC3B,CAAC,CAAC;IACL,CAAC;IAGY,AAAN,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,gBAAuC,EAAE,UAAmB;QAC7F,MAAM,CAAC,OAAO,CAAC,iDAAiD,KAAK,CAAC,MAAM,WAAW,gBAAgB,EAAE,MAAM,IAAI,CAAC,8BAA8B,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;QAEhL,MAAM,aAAa,GAAG,gBAAgB,EAAE,MAAM;YAC5C,CAAC,CAAC,wEAAwE,gBAAgB;iBACrF,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC;iBACzC,IAAI,CAAC,IAAI,CAAC,EAAE;YACjB,CAAC,CAAC,EAAE,CAAC;QAEP,MAAM,QAAQ,GAAG,UAAU,EAAE,IAAI,EAAE;YACjC,CAAC,CAAC,4FAA4F,UAAU,CAAC,IAAI,EAAE,EAAE;YACjH,CAAC,CAAC,EAAE,CAAC;QAEP,MAAM,MAAM,GAAG,6DAA6D,KAAK,GAAG,aAAa,GAAG,QAAQ,EAAE,CAAC;QAE/G,MAAM,QAAQ,GAAG;YACf,IAAI,aAAa,CAAC,YAAY,CAAC;YAC/B,IAAI,YAAY,CAAC,MAAM,CAAC;SACzB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACjE,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAE5C,4FAA4F;QAC5F,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACpE,MAAM,UAAU,GAAG,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAChF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,sCAAsC,UAAU,CAAC,MAAM,2BAA2B,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QAClH,CAAC;QACD,MAAM,CAAC,mBAAmB,GAAG,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAEzF,uEAAuE;QACvE,yEAAyE;QACzE,4CAA4C;QAC5C,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC;QAChD,IACE,CAAC,UAAU,EAAE,IAAI,EAAE;YACnB,CAAC,aAAa;YACd,aAAa,CAAC,WAAW,EAAE,KAAK,MAAM;YACtC,aAAa,KAAK,UAAU,CAAC,IAAI,EAAE,EACnC,CAAC;YACD,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC;QAC3B,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,UAAU,GAAG,aAAa,CAAC;QACpC,CAAC;QAED,MAAM,CAAC,OAAO,CAAC,wCAAwC,MAAM,CAAC,QAAQ,CAAC,MAAM,gBAAgB,MAAM,CAAC,mBAAmB,CAAC,MAAM,+BAA+B,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACjM,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AAjDc;IADZ,KAAK,EAAE;;;;+CAiDP","sourcesContent":["import { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { z } from \"zod\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { createStructuredModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\nconst logger = protocolLogger(\"PremiseDecomposer\");\n\nconst systemPrompt = `\nYou are the Premise Decomposer for the Index Network — an intent-driven discovery protocol.\n\nYour job: decompose free-text input about a person into individual, atomic premises.\n\nA premise is a single self-descriptive proposition — a fact someone asserts about themselves.\nEach premise should be:\n1. **Atomic** — one fact per premise, not compound statements\n2. **First-person** — phrased as \"I am...\", \"I work at...\", \"I specialize in...\"\n3. **Self-descriptive** — about the person's identity, role, skills, interests, location, or context\n4. **Non-redundant** — do not repeat the same information in multiple premises\n\n═══════════════════════════════════════════════════\nINPUT TYPES\n═══════════════════════════════════════════════════\n\nYou may receive:\n- **Chat messages**: \"I'm a software engineer at Google, based in SF. I'm interested in AI safety.\"\n- **Bio text**: A paragraph describing someone's background\n- **Scraped content**: LinkedIn/GitHub/Twitter data about someone\n- **Mixed input**: Name, location, and free-text combined\n\n═══════════════════════════════════════════════════\nDECOMPOSITION RULES\n═══════════════════════════════════════════════════\n\n1. Extract every distinct factual claim about the person\n2. Convert third-person (\"She works at...\") to first-person (\"I work at...\")\n3. Separate compound statements: \"I know Python and Rust\" → two premises\n4. Capture the FULL breadth of self-description, not just job/title. In particular,\n do not drop:\n - **Skills & expertise** (\"I specialize in distributed systems\", \"I know Rust\")\n - **Interests & focus areas** (\"I am interested in AI safety\", \"I care about climate\")\n - **Background/narrative context** (education, prior roles, domains, affiliations)\n These map to what a profile would store as interests/skills/context — every such\n fact must surface as its own premise so nothing is lost.\n5. Classify each premise:\n - \\`assertive\\` — stable identity facts (role, skills, interests, education, location)\n - \\`contextual\\` — temporal/situational (current projects, events, fundraising status)\n6. For \\`contextual\\` premises, estimate \\`validityDays\\` — how many days the fact is\n likely to remain true from now (e.g. \"speaking next week\" → ~7, \"raising a Series A\"\n → ~120, \"currently building X\" → ~180). Use null for \\`assertive\\` premises, which\n have no natural expiry.\n7. Skip vague or uninformative statements (\"I like stuff\", \"I'm a person\")\n8. Skip desires, requests, or intents (\"I'm looking for...\", \"I want to...\") — those are intents, not premises\n9. If the input contains NO extractable premises (e.g. just \"Yes\" or \"Hello\"), return an empty array\n10. NEVER extract denials or removal instructions as premises. \"I have nothing to do with X\",\n \"I am not a Y\", or \"remove X from my profile\" are retractions, not facts — handle them\n via the retraction rules below and do NOT emit a premise for them.\n\n═════════════════════════════════════════════════\nRETRACTION RULES\n═════════════════════════════════════════════════\n\nWhen the message includes the user's EXISTING PREMISES (listed with ids), you must also\ndetect which of them the input disavows and return their ids in \\`retractedPremiseIds\\`.\n\nA premise must be retracted when the input:\n- Explicitly asks to remove it (\"remove all mentions of X\", \"delete the part about Y\")\n- Denies it (\"I have nothing to do with X\", \"I never worked at Y\", \"that's not true\")\n- States it no longer holds (\"I no longer live in Berlin\", \"I left Google\")\n- Directly contradicts it (\"I'm based in Istanbul\" contradicts \"I am based in Ankara\")\n\nRules:\n- Only return ids that appear in the provided EXISTING PREMISES list — never invent ids.\n- Retract EVERY existing premise that matches the disavowed topic, not just the first one.\n- Do not retract premises the input merely omits — silence is not disavowal.\n- If no existing premises are provided, or nothing is disavowed, return an empty array.\n- Retraction and extraction are independent: an input can retract old premises AND\n contribute new ones in the same pass.\n\n═══════════════════════════════════════════════════\nBIO REVISION RULES\n═══════════════════════════════════════════════════\n\nWhen the message includes the user's CURRENT BIO, check whether the input disavows,\nremoves, or corrects anything that appears in it.\n\n- If it does, return \\`revisedBio\\`: the bio rewritten with those facts removed or\n corrected. Preserve everything the input does not dispute — same tone, similar\n length, no inventions.\n- If the input explicitly rewrites the bio (\"update my bio to X\"), return that\n rewritten bio.\n- NEVER add new facts to the bio — new information becomes premises, not bio edits.\n Purely additive input (\"I also enjoy X\") must return revisedBio: null.\n- If the bio is unaffected, or no CURRENT BIO was provided, return null.\n- Never return a revised bio that still mentions a disavowed fact.\n\n═══════════════════════════════════════════════════\nEXAMPLES\n═══════════════════════════════════════════════════\n\nInput: \"I'm a climate tech founder based in Berlin. I have a PhD in renewable energy systems and I'm currently raising Series A.\"\nOutput:\n 1. \"I am a climate tech founder\" (assertive, validityDays: null)\n 2. \"I am based in Berlin\" (assertive, validityDays: null)\n 3. \"I hold a PhD in renewable energy systems\" (assertive, validityDays: null)\n 4. \"I am currently raising Series A\" (contextual, validityDays: 120)\n\nInput: \"John Doe is a senior ML engineer at Meta in Menlo Park. He has 8 years of experience in NLP and computer vision.\"\nOutput:\n 1. \"I am a senior ML engineer\" (assertive, validityDays: null)\n 2. \"I work at Meta\" (assertive, validityDays: null)\n 3. \"I am based in Menlo Park\" (assertive, validityDays: null)\n 4. \"I have 8 years of experience in NLP\" (assertive, validityDays: null)\n 5. \"I have experience in computer vision\" (assertive, validityDays: null)\n\nInput: \"Yes, create my profile\"\nOutput: [] (empty — no premises)\n\nInput: \"Remove all mentions of the HOPE programming language. I have nothing to do with it. I specialize in compiler design.\"\nExisting premises:\n - id: aaaa-1 · \"I am the creator of the HOPE programming language\"\n - id: aaaa-2 · \"I specialize in compiler design\"\n - id: aaaa-3 · \"I am based in Istanbul\"\nCurrent bio: \"Software engineer in Istanbul. Creator of the HOPE programming language. Specializes in compiler design.\"\nOutput:\n premises: [] (compiler design already exists as aaaa-2 — nothing new to add)\n retractedPremiseIds: [\"aaaa-1\"]\n revisedBio: \"Software engineer in Istanbul. Specializes in compiler design.\"\n`;\n\nconst premiseItemSchema = z.object({\n text: z.string().describe(\"The premise text in first person (e.g. 'I am a software engineer at Google')\"),\n tier: z.enum([\"assertive\", \"contextual\"]).describe(\n \"assertive = stable identity fact; contextual = temporal/situational\"\n ),\n validityDays: z.number().int().positive().nullable().describe(\n \"For contextual premises: estimated days the fact remains true from now (its validity window). null for assertive premises, which do not expire.\"\n ),\n});\n\nconst responseFormat = z.object({\n reasoning: z.string().describe(\n \"Brief analysis of what factual claims can be extracted from the input\"\n ),\n premises: z.array(premiseItemSchema).describe(\n \"Array of extracted premises. Empty if input contains no self-descriptive facts.\"\n ),\n retractedPremiseIds: z.array(z.string()).default([]).describe(\n \"Ids of EXISTING premises (from the provided list) that the input disavows, denies, or asks to remove. Empty when nothing is disavowed or no existing premises were provided.\"\n ),\n revisedBio: z.string().nullable().default(null).describe(\n \"When the input disavows or corrects facts that appear in the provided CURRENT BIO, the bio rewritten without those facts (preserving everything else). null when the bio is unaffected or no bio was provided.\"\n ),\n});\n\n/** An existing ACTIVE premise offered to the decomposer for retraction matching. */\nexport interface ExistingPremiseRef {\n id: string;\n text: string;\n}\n\nexport type PremiseDecomposerOutput = z.infer<typeof responseFormat>;\nexport type DecomposedPremise = z.infer<typeof premiseItemSchema>;\n\n/**\n * Decomposes free-text input (chat messages, bios, scraped content) into\n * individual atomic premises suitable for the premise graph.\n */\nexport class PremiseDecomposer {\n private model: ReturnType<typeof createStructuredModel>;\n\n constructor() {\n this.model = createStructuredModel(\"premiseDecomposer\", responseFormat, {\n name: \"premise_decomposer\",\n });\n }\n\n @Timed()\n public async invoke(input: string, existingPremises?: ExistingPremiseRef[], currentBio?: string): Promise<PremiseDecomposerOutput> {\n logger.verbose(`[PremiseDecomposer.invoke] Decomposing input (${input.length} chars, ${existingPremises?.length ?? 0} existing premise(s), bio: ${currentBio ? 'yes' : 'no'})`);\n\n const existingBlock = existingPremises?.length\n ? `\\n\\nEXISTING PREMISES (retract by id when the input disavows them):\\n${existingPremises\n .map((p) => `- id: ${p.id} · \"${p.text}\"`)\n .join(\"\\n\")}`\n : \"\";\n\n const bioBlock = currentBio?.trim()\n ? `\\n\\nCURRENT BIO (return revisedBio when the input disavows or corrects anything in it):\\n${currentBio.trim()}`\n : \"\";\n\n const prompt = `Decompose the following text into individual premises:\\n\\n${input}${existingBlock}${bioBlock}`;\n\n const messages = [\n new SystemMessage(systemPrompt),\n new HumanMessage(prompt),\n ];\n\n const result = await invokeWithAbortSignal(this.model, messages);\n const output = responseFormat.parse(result);\n\n // Guard against hallucinated ids: only keep retractions that reference premises we offered.\n const knownIds = new Set((existingPremises ?? []).map((p) => p.id));\n const droppedIds = output.retractedPremiseIds.filter((id) => !knownIds.has(id));\n if (droppedIds.length > 0) {\n logger.warn(`[PremiseDecomposer.invoke] Dropped ${droppedIds.length} unknown retraction id(s)`, { droppedIds });\n }\n output.retractedPremiseIds = output.retractedPremiseIds.filter((id) => knownIds.has(id));\n\n // A revised bio is only meaningful when we offered one to revise. Also\n // normalize structured-output quirks: literal \"null\", empty strings, and\n // no-op rewrites all mean \"bio unaffected\".\n const normalizedBio = output.revisedBio?.trim();\n if (\n !currentBio?.trim() ||\n !normalizedBio ||\n normalizedBio.toLowerCase() === 'null' ||\n normalizedBio === currentBio.trim()\n ) {\n output.revisedBio = null;\n } else {\n output.revisedBio = normalizedBio;\n }\n\n logger.verbose(`[PremiseDecomposer.invoke] Extracted ${output.premises.length} premise(s), ${output.retractedPremiseIds.length} retraction(s), revisedBio: ${output.revisedBio ? 'yes' : 'no'}`);\n return output;\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"premise.indexer.d.ts","sourceRoot":"/","sources":["premise/premise.indexer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgCxB,QAAA,MAAM,cAAc;;;;;;;;;;;;EAIlB,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAElE;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,
|
|
1
|
+
{"version":3,"file":"premise.indexer.d.ts","sourceRoot":"/","sources":["premise/premise.indexer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgCxB,QAAA,MAAM,cAAc;;;;;;;;;;;;EAIlB,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAElE;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAA2C;;IAQxD;;;;;OAKG;IAEU,MAAM,CAAC,KAAK,EAAE;QACzB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAuBlC"}
|
|
@@ -11,7 +11,7 @@ import { HumanMessage, SystemMessage } from "@langchain/core/messages";
|
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
import { protocolLogger } from "../shared/observability/protocol.logger.js";
|
|
13
13
|
import { Timed } from "../shared/observability/performance.js";
|
|
14
|
-
import {
|
|
14
|
+
import { createStructuredModel } from "../shared/agent/model.config.js";
|
|
15
15
|
import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
|
|
16
16
|
const logger = protocolLogger("PremiseIndexer");
|
|
17
17
|
const systemPrompt = `
|
|
@@ -47,8 +47,7 @@ const responseFormat = z.object({
|
|
|
47
47
|
*/
|
|
48
48
|
export class PremiseIndexer {
|
|
49
49
|
constructor() {
|
|
50
|
-
|
|
51
|
-
this.model = model.withStructuredOutput(responseFormat, {
|
|
50
|
+
this.model = createStructuredModel("premiseIndexer", responseFormat, {
|
|
52
51
|
name: "premise_indexer"
|
|
53
52
|
});
|
|
54
53
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"premise.indexer.js","sourceRoot":"/","sources":["premise/premise.indexer.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AAC5E,OAAO,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAC/D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"premise.indexer.js","sourceRoot":"/","sources":["premise/premise.indexer.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AAC5E,OAAO,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAExE,MAAM,MAAM,GAAG,cAAc,CAAC,gBAAgB,CAAC,CAAC;AAEhD,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;CAsBpB,CAAC;AAEF,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,qCAAqC,CAAC;IACpF,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,6CAA6C,CAAC;IAC7F,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;CACjE,CAAC,CAAC;AAIH;;GAEG;AACH,MAAM,OAAO,cAAc;IAGzB;QACE,IAAI,CAAC,KAAK,GAAG,qBAAqB,CAAC,gBAAgB,EAAE,cAAc,EAAE;YACnE,IAAI,EAAE,iBAAiB;SACxB,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IAEU,AAAN,KAAK,CAAC,MAAM,CAAC,KAKnB;QACC,MAAM,CAAC,OAAO,CAAC,uDAAuD,CAAC,CAAC;QAExE,MAAM,MAAM,GAAG;YACb,WAAW;YACX,KAAK,CAAC,WAAW;YACjB,EAAE;YACF,kBAAkB;YAClB,KAAK,CAAC,WAAW,IAAI,8BAA8B;YACnD,EAAE;YACF,iBAAiB;YACjB,KAAK,CAAC,YAAY,IAAI,6BAA6B;YACnD,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,mBAAmB,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;SACjF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEb,MAAM,QAAQ,GAAG;YACf,IAAI,aAAa,CAAC,YAAY,CAAC;YAC/B,IAAI,YAAY,CAAC,MAAM,CAAC;SACzB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACjE,OAAO,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;CACF;AA5Bc;IADZ,KAAK,EAAE;;;;4CA4BP","sourcesContent":["import { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { z } from \"zod\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { createStructuredModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\n\nconst logger = protocolLogger(\"PremiseIndexer\");\n\nconst systemPrompt = `\nYou are a Premise Evaluator for a social networking protocol.\n\nTASK:\nDetermine if a User Premise (a self-descriptive proposition about who someone is) is relevant to a specific Index (community).\n\nINPUTS:\n1. Premise: A self-description the user asserts about themselves.\n2. Network Prompt: The purpose/scope of the target community (Index).\n3. Member Prompt: The specific sharing preferences of the user in that community (optional).\n\nSCORING RUBRIC:\n- 0.9-1.0: Highly relevant. The premise directly relates to the community's purpose.\n- 0.7-0.8: Good relevance. The premise is clearly adjacent to the community's focus.\n- 0.5-0.6: Moderate. Borderline relevance.\n- 0.3-0.4: Low relevance. Weak connection.\n- 0.0-0.2: Not relevant. The premise has no connection to this community.\n\nOUTPUT RULES:\n- Provide indexScore based on how well the Premise fits the Network Prompt.\n- Provide memberScore based on how well the Premise fits the Member Prompt (if provided). If Member Prompt is missing/empty, return 0.0.\n- Provide concise reasoning.\n`;\n\nconst responseFormat = z.object({\n indexScore: z.number().min(0).max(1).describe(\"Score for index relevance (0.0-1.0)\"),\n memberScore: z.number().min(0).max(1).describe(\"Score for member preference match (0.0-1.0)\"),\n reasoning: z.string().describe(\"Brief reasoning for the scores\"),\n});\n\nexport type PremiseIndexerOutput = z.infer<typeof responseFormat>;\n\n/**\n * Scores a premise's relevancy to a network based on the index and member prompts.\n */\nexport class PremiseIndexer {\n private model: ReturnType<typeof createStructuredModel>;\n\n constructor() {\n this.model = createStructuredModel(\"premiseIndexer\", responseFormat, {\n name: \"premise_indexer\"\n });\n }\n\n /**\n * Scores the relevancy of a premise to a network index and member preferences.\n *\n * @param input - The premise text, network prompt, member prompt, and optional network context.\n * @returns Structured output with indexScore, memberScore, and reasoning.\n */\n @Timed()\n public async invoke(input: {\n premiseText: string;\n indexPrompt: string;\n memberPrompt?: string;\n networkContext?: string;\n }): Promise<PremiseIndexerOutput> {\n logger.verbose(`[PremiseIndexer.invoke] Scoring premise against index`);\n\n const prompt = [\n \"# Premise\",\n input.premiseText,\n \"\",\n \"# Network Prompt\",\n input.indexPrompt || \"(No network prompt provided)\",\n \"\",\n \"# Member Prompt\",\n input.memberPrompt || \"(No member prompt provided)\",\n ...(input.networkContext ? [\"\", \"# Network Context\", input.networkContext] : []),\n ].join(\"\\n\");\n\n const messages = [\n new SystemMessage(systemPrompt),\n new HumanMessage(prompt),\n ];\n\n const result = await invokeWithAbortSignal(this.model, messages);\n return responseFormat.parse(result);\n }\n}\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type QuestionGenerationResult } from "../shared/schemas/question.schema.js";
|
|
2
|
-
import {
|
|
2
|
+
import { createStructuredModel } from "../shared/agent/model.config.js";
|
|
3
3
|
import type { QuestionerInput } from "./questioner.types.js";
|
|
4
4
|
export interface QuestionerAgentConfig {
|
|
5
5
|
/** Optional model config override. */
|
|
6
|
-
modelConfig?: Parameters<typeof
|
|
6
|
+
modelConfig?: Parameters<typeof createStructuredModel>[3];
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
9
|
* Stateless question-generation agent. Accepts a `QuestionerInput` envelope,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"questioner.agent.d.ts","sourceRoot":"/","sources":["questioner/questioner.agent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"questioner.agent.d.ts","sourceRoot":"/","sources":["questioner/questioner.agent.ts"],"names":[],"mappings":"AAWA,OAAO,EAAkD,KAAK,wBAAwB,EAAoD,MAAM,sCAAsC,CAAC;AACvL,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAKxE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAO7D,MAAM,WAAW,qBAAqB;IACpC,sCAAsC;IACtC,WAAW,CAAC,EAAE,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC;CAC3D;AAED;;;;GAIG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,KAAK,CAA2C;gBAE5C,MAAM,CAAC,EAAE,qBAAqB;IAM1C;;;;;;;;OAQG;IAEG,MAAM,CACV,KAAK,EAAE,eAAe,EACtB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC;CA8C5C"}
|
|
@@ -7,9 +7,18 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
|
+
/**
|
|
11
|
+
* QuestionerAgent — stateless, mode-driven agent that generates structured
|
|
12
|
+
* decision questions from arbitrary protocol contexts.
|
|
13
|
+
*
|
|
14
|
+
* Follows the IndexNegotiator pattern: constructor takes optional config,
|
|
15
|
+
* single public `invoke()` method receives the full context per call.
|
|
16
|
+
* The LLM model is bound once at construction; the preset (system prompt +
|
|
17
|
+
* builder) is selected per invocation based on `input.mode`.
|
|
18
|
+
*/
|
|
10
19
|
import { HumanMessage, SystemMessage } from "@langchain/core/messages";
|
|
11
20
|
import { QuestionGeneratorResponseSchema } from "../shared/schemas/question.schema.js";
|
|
12
|
-
import {
|
|
21
|
+
import { createStructuredModel } from "../shared/agent/model.config.js";
|
|
13
22
|
import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
|
|
14
23
|
import { protocolLogger } from "../shared/observability/protocol.logger.js";
|
|
15
24
|
import { Timed } from "../shared/observability/performance.js";
|
|
@@ -24,10 +33,9 @@ const MAX_SAME_STRATEGY = 2;
|
|
|
24
33
|
*/
|
|
25
34
|
export class QuestionerAgent {
|
|
26
35
|
constructor(config) {
|
|
27
|
-
|
|
28
|
-
this.model = llm.withStructuredOutput(QuestionGeneratorResponseSchema, {
|
|
36
|
+
this.model = createStructuredModel("questioner", QuestionGeneratorResponseSchema, {
|
|
29
37
|
name: "clarifying_questions",
|
|
30
|
-
});
|
|
38
|
+
}, config?.modelConfig);
|
|
31
39
|
}
|
|
32
40
|
/**
|
|
33
41
|
* Generate up to 3 decision questions from the given input.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"questioner.agent.js","sourceRoot":"/","sources":["questioner/questioner.agent.ts"],"names":[],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"questioner.agent.js","sourceRoot":"/","sources":["questioner/questioner.agent.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * QuestionerAgent — stateless, mode-driven agent that generates structured\n * decision questions from arbitrary protocol contexts.\n *\n * Follows the IndexNegotiator pattern: constructor takes optional config,\n * single public `invoke()` method receives the full context per call.\n * The LLM model is bound once at construction; the preset (system prompt +\n * builder) is selected per invocation based on `input.mode`.\n */\nimport { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\n\nimport { QuestionGeneratorResponseSchema, type Question, type QuestionGenerationResult, type QuestionStrategy, type QuestionWithStrategy } from \"../shared/schemas/question.schema.js\";\nimport { createStructuredModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\nimport { Timed } from \"../shared/observability/performance.js\";\nimport { getPreset } from \"./questioner.presets.js\";\nimport type { QuestionerInput } from \"./questioner.types.js\";\n\nconst logger = protocolLogger(\"QuestionerAgent\");\n\n/** Maximum same-strategy questions allowed in a single emission. */\nconst MAX_SAME_STRATEGY = 2;\n\nexport interface QuestionerAgentConfig {\n /** Optional model config override. */\n modelConfig?: Parameters<typeof createStructuredModel>[3];\n}\n\n/**\n * Stateless question-generation agent. Accepts a `QuestionerInput` envelope,\n * selects the preset for the given mode, invokes the LLM, and applies\n * guardrails (dedup + strategy diversity).\n */\nexport class QuestionerAgent {\n private model: ReturnType<typeof createStructuredModel>;\n\n constructor(config?: QuestionerAgentConfig) {\n this.model = createStructuredModel(\"questioner\", QuestionGeneratorResponseSchema, {\n name: \"clarifying_questions\",\n }, config?.modelConfig);\n }\n\n /**\n * Generate up to 3 decision questions from the given input.\n *\n * @param input Envelope with mode, userId, source info, and mode-specific context.\n * @param options.signal Optional AbortSignal to cancel the in-flight LLM call.\n * @returns A result with parallel questions[] and strategies[] arrays,\n * or null when generation failed, guardrails dropped all candidates,\n * the LLM threw, or the call was aborted.\n */\n @Timed()\n async invoke(\n input: QuestionerInput,\n options?: { signal?: AbortSignal },\n ): Promise<QuestionGenerationResult | null> {\n const preset = getPreset(input.mode);\n const userMessage = preset.buildPrompt(input.context);\n\n let raw: unknown;\n try {\n raw = await invokeWithAbortSignal(\n this.model,\n [new SystemMessage(preset.systemPrompt), new HumanMessage(userMessage)],\n options?.signal,\n );\n } catch (err) {\n const aborted = options?.signal?.aborted ?? false;\n if (aborted) {\n logger.info(\"QuestionerAgent aborted by signal\", {\n mode: input.mode,\n reason: options?.signal?.reason instanceof Error\n ? options.signal.reason.message\n : String(options?.signal?.reason ?? \"unknown\"),\n });\n } else {\n logger.warn(\"QuestionerAgent LLM call failed\", {\n mode: input.mode,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n return null;\n }\n\n const parsed = QuestionGeneratorResponseSchema.safeParse(raw);\n if (!parsed.success) {\n logger.warn(\"QuestionerAgent parse failed\", {\n mode: input.mode,\n error: parsed.error.message,\n });\n return null;\n }\n\n const filtered = applyGuardrails(parsed.data.questions);\n if (filtered.length === 0) return null;\n\n return {\n questions: filtered.map(stripStrategy),\n strategies: filtered.map((q) => q.strategy),\n };\n }\n}\n\n// --- Guardrails (migrated from question.generator.ts) -------------------------\n\nfunction applyGuardrails(questions: QuestionWithStrategy[]): QuestionWithStrategy[] {\n const dedupedByTitle = dedupByTitle(questions);\n return enforceStrategyDiversity(dedupedByTitle);\n}\n\nfunction dedupByTitle(questions: QuestionWithStrategy[]): QuestionWithStrategy[] {\n const seen = new Set<string>();\n const out: QuestionWithStrategy[] = [];\n for (const q of questions) {\n if (seen.has(q.title)) continue;\n seen.add(q.title);\n out.push(q);\n }\n return out;\n}\n\nfunction enforceStrategyDiversity(\n questions: QuestionWithStrategy[],\n): QuestionWithStrategy[] {\n const counts = new Map<QuestionStrategy, number>();\n const out: QuestionWithStrategy[] = [];\n for (const q of questions) {\n const n = counts.get(q.strategy) ?? 0;\n if (n >= MAX_SAME_STRATEGY) continue;\n counts.set(q.strategy, n + 1);\n out.push(q);\n }\n return out;\n}\n\nfunction stripStrategy(q: QuestionWithStrategy): Question {\n const { strategy: _strategy, ...publicShape } = q;\n return publicShape;\n}\n"]}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { ChatOpenAI } from "@langchain/openai";
|
|
2
|
+
import type { AIMessageChunk } from "@langchain/core/messages";
|
|
3
|
+
import type { BaseLanguageModelInput, StructuredOutputMethodOptions } from "@langchain/core/language_models/base";
|
|
4
|
+
import type { Runnable } from "@langchain/core/runnables";
|
|
5
|
+
import type { InteropZodType } from "@langchain/core/utils/types";
|
|
2
6
|
/** Settings that can be configured per agent. */
|
|
3
7
|
export interface ModelSettings {
|
|
4
8
|
model: string;
|
|
@@ -137,17 +141,52 @@ declare function getModelConfig(config?: ModelConfig): {
|
|
|
137
141
|
};
|
|
138
142
|
};
|
|
139
143
|
};
|
|
144
|
+
/** Key identifying one of the per-agent model configurations. */
|
|
145
|
+
export type ModelAgent = keyof ReturnType<typeof getModelConfig>;
|
|
140
146
|
/**
|
|
141
147
|
* Returns the model name string for the given agent key.
|
|
142
148
|
* @param agent - Key from MODEL_CONFIG identifying which agent's settings to use.
|
|
143
149
|
* @param config - Optional runtime config overrides.
|
|
144
150
|
*/
|
|
145
|
-
export declare function getModelName(agent:
|
|
151
|
+
export declare function getModelName(agent: ModelAgent, config?: ModelConfig): string;
|
|
146
152
|
/**
|
|
147
153
|
* Creates a ChatOpenAI instance configured for OpenRouter.
|
|
148
154
|
* @param agent - Key identifying which agent's model settings to use.
|
|
149
155
|
* @param config - Optional runtime config overrides.
|
|
150
156
|
*/
|
|
151
|
-
export declare function createModel(agent:
|
|
157
|
+
export declare function createModel(agent: ModelAgent, config?: ModelConfig): ChatOpenAI;
|
|
158
|
+
/**
|
|
159
|
+
* Creates the fallback ChatOpenAI for an agent, or undefined when fallbacks
|
|
160
|
+
* are disabled or the fallback would be the same model as the primary.
|
|
161
|
+
* Reuses the agent's sampling settings but drops `reasoning` kwargs, which are
|
|
162
|
+
* primary-model specific.
|
|
163
|
+
*/
|
|
164
|
+
export declare function createFallbackModel(agent: ModelAgent, config?: ModelConfig): ChatOpenAI | undefined;
|
|
165
|
+
/**
|
|
166
|
+
* Creates a structured-output model with runnable-level retry and cross-model
|
|
167
|
+
* fallback. Equivalent to `createModel(agent).withStructuredOutput(schema, options)`
|
|
168
|
+
* plus `.withRetry(...)` and `.withFallbacks([...])`.
|
|
169
|
+
*
|
|
170
|
+
* Retry covers transient provider errors *and* schema parse/validation
|
|
171
|
+
* failures; the fallback model (see OPENROUTER_FALLBACK_MODEL) is bound to the
|
|
172
|
+
* same schema so a provider outage degrades to a different vendor instead of
|
|
173
|
+
* failing the call. Abort signals pass through: aborts are never retried and
|
|
174
|
+
* skip the fallback.
|
|
175
|
+
*
|
|
176
|
+
* @param agent - Key identifying which agent's model settings to use.
|
|
177
|
+
* @param outputSchema - Zod schema or JSON-schema response format.
|
|
178
|
+
* @param options - Same options as `withStructuredOutput` (e.g. `{ name }`).
|
|
179
|
+
* @param config - Optional runtime model config overrides.
|
|
180
|
+
*/
|
|
181
|
+
export declare function createStructuredModel<RunOutput extends Record<string, any> = Record<string, any>>(agent: ModelAgent, outputSchema: InteropZodType<RunOutput> | Record<string, unknown>, options?: StructuredOutputMethodOptions<false>, config?: ModelConfig): Runnable<BaseLanguageModelInput, RunOutput>;
|
|
182
|
+
/**
|
|
183
|
+
* Creates a plain-completion model with runnable-level retry and cross-model
|
|
184
|
+
* fallback, for call sites that `invoke()` the model directly (no
|
|
185
|
+
* `withStructuredOutput`/`bindTools`/`stream` chaining).
|
|
186
|
+
*
|
|
187
|
+
* @param agent - Key identifying which agent's model settings to use.
|
|
188
|
+
* @param config - Optional runtime model config overrides.
|
|
189
|
+
*/
|
|
190
|
+
export declare function createResilientModel(agent: ModelAgent, config?: ModelConfig): Runnable<BaseLanguageModelInput, AIMessageChunk>;
|
|
152
191
|
export {};
|
|
153
192
|
//# sourceMappingURL=model.config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.config.d.ts","sourceRoot":"/","sources":["shared/agent/model.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"model.config.d.ts","sourceRoot":"/","sources":["shared/agent/model.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,MAAM,sCAAsC,CAAC;AAClH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC7F;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,mBAAmB,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;CACvE;AAED,iBAAS,cAAc,CAAC,MAAM,CAAC,EAAE,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BAgCmD,WAAW,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC;;;;EAK/I;AAED,iEAAiE;AACjE,MAAM,MAAM,UAAU,GAAG,MAAM,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAEjE;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,CAE5E;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,UAAU,CAG/E;AAmDD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,SAAS,CAMnG;AAkCD;;;;;;;;;;;;;;;GAeG;AAEH,wBAAgB,qBAAqB,CAAC,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/F,KAAK,EAAE,UAAU,EACjB,YAAY,EAAE,cAAc,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,OAAO,CAAC,EAAE,6BAA6B,CAAC,KAAK,CAAC,EAC9C,MAAM,CAAC,EAAE,WAAW,GACnB,QAAQ,CAAC,sBAAsB,EAAE,SAAS,CAAC,CAK7C;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,UAAU,EACjB,MAAM,CAAC,EAAE,WAAW,GACnB,QAAQ,CAAC,sBAAsB,EAAE,cAAc,CAAC,CAElD"}
|
|
@@ -51,11 +51,15 @@ export function getModelName(agent, config) {
|
|
|
51
51
|
* @param config - Optional runtime config overrides.
|
|
52
52
|
*/
|
|
53
53
|
export function createModel(agent, config) {
|
|
54
|
+
const cfg = getModelConfig(config)[agent];
|
|
55
|
+
return instantiateModel(agent, cfg, config);
|
|
56
|
+
}
|
|
57
|
+
/** Instantiates a ChatOpenAI from explicit settings (shared by primary + fallback creation). */
|
|
58
|
+
function instantiateModel(agent, cfg, config) {
|
|
54
59
|
const apiKey = config?.apiKey ?? process.env.OPENROUTER_API_KEY;
|
|
55
60
|
if (!apiKey?.trim()) {
|
|
56
61
|
throw new Error(`createModel(${agent}): OPENROUTER_API_KEY is required. Pass via the config argument, ToolContext.modelConfig.apiKey, or set the OPENROUTER_API_KEY environment variable.`);
|
|
57
62
|
}
|
|
58
|
-
const cfg = getModelConfig(config)[agent];
|
|
59
63
|
// Hard upper bound on a single LLM call. Without this, langchain's HTTP
|
|
60
64
|
// client waits until the upstream cuts the socket (~3 minutes via
|
|
61
65
|
// OpenRouter), blocking the entire chat response. 60 s is generous enough
|
|
@@ -82,4 +86,97 @@ export function createModel(agent, config) {
|
|
|
82
86
|
...(cfg.reasoning && { modelKwargs: { reasoning: cfg.reasoning } }),
|
|
83
87
|
});
|
|
84
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Default cross-vendor fallback model. The per-agent primaries run on Google's
|
|
91
|
+
* provider lane (gemini-2.5-flash); a same-key OpenRouter fallback on a
|
|
92
|
+
* different vendor survives Google-side outages.
|
|
93
|
+
* Override via OPENROUTER_FALLBACK_MODEL; set it to "none" (or "off") to disable.
|
|
94
|
+
*/
|
|
95
|
+
const DEFAULT_FALLBACK_MODEL = "openai/gpt-4o-mini";
|
|
96
|
+
function getFallbackModelName() {
|
|
97
|
+
const raw = process.env.OPENROUTER_FALLBACK_MODEL;
|
|
98
|
+
if (raw === undefined)
|
|
99
|
+
return DEFAULT_FALLBACK_MODEL;
|
|
100
|
+
const value = raw.trim();
|
|
101
|
+
if (!value || value.toLowerCase() === "none" || value.toLowerCase() === "off")
|
|
102
|
+
return undefined;
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Creates the fallback ChatOpenAI for an agent, or undefined when fallbacks
|
|
107
|
+
* are disabled or the fallback would be the same model as the primary.
|
|
108
|
+
* Reuses the agent's sampling settings but drops `reasoning` kwargs, which are
|
|
109
|
+
* primary-model specific.
|
|
110
|
+
*/
|
|
111
|
+
export function createFallbackModel(agent, config) {
|
|
112
|
+
const fallbackName = getFallbackModelName();
|
|
113
|
+
if (!fallbackName)
|
|
114
|
+
return undefined;
|
|
115
|
+
const cfg = getModelConfig(config)[agent];
|
|
116
|
+
if (cfg.model === fallbackName)
|
|
117
|
+
return undefined;
|
|
118
|
+
return instantiateModel(agent, { ...cfg, model: fallbackName, reasoning: undefined }, config);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Number of attempts (1 = no retry) for runnable-level retries added by
|
|
122
|
+
* `createStructuredModel` / `createResilientModel`. These wrap ChatOpenAI's
|
|
123
|
+
* own HTTP-level `maxRetries` and additionally cover structured-output
|
|
124
|
+
* parse/validation failures. Configurable via OPENROUTER_RUNNABLE_MAX_ATTEMPTS.
|
|
125
|
+
*/
|
|
126
|
+
function getRunnableMaxAttempts() {
|
|
127
|
+
const env = Number.parseInt(process.env.OPENROUTER_RUNNABLE_MAX_ATTEMPTS ?? "", 10);
|
|
128
|
+
return Number.isFinite(env) && env >= 1 ? env : 2;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Stops runnable-level retries when the failure was a caller abort —
|
|
132
|
+
* retrying a cancelled request only delays cancellation. Thrown errors from
|
|
133
|
+
* `onFailedAttempt` abort the retry loop.
|
|
134
|
+
*/
|
|
135
|
+
function abortAwareFailedAttemptHandler(error) {
|
|
136
|
+
if (error?.name === "AbortError" || error?.name === "APIUserAbortError")
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
function withResilience(primary, fallback) {
|
|
140
|
+
const attempts = getRunnableMaxAttempts();
|
|
141
|
+
let runnable = attempts > 1
|
|
142
|
+
? primary.withRetry({ stopAfterAttempt: attempts, onFailedAttempt: abortAwareFailedAttemptHandler })
|
|
143
|
+
: primary;
|
|
144
|
+
if (fallback)
|
|
145
|
+
runnable = runnable.withFallbacks([fallback]);
|
|
146
|
+
return runnable;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Creates a structured-output model with runnable-level retry and cross-model
|
|
150
|
+
* fallback. Equivalent to `createModel(agent).withStructuredOutput(schema, options)`
|
|
151
|
+
* plus `.withRetry(...)` and `.withFallbacks([...])`.
|
|
152
|
+
*
|
|
153
|
+
* Retry covers transient provider errors *and* schema parse/validation
|
|
154
|
+
* failures; the fallback model (see OPENROUTER_FALLBACK_MODEL) is bound to the
|
|
155
|
+
* same schema so a provider outage degrades to a different vendor instead of
|
|
156
|
+
* failing the call. Abort signals pass through: aborts are never retried and
|
|
157
|
+
* skip the fallback.
|
|
158
|
+
*
|
|
159
|
+
* @param agent - Key identifying which agent's model settings to use.
|
|
160
|
+
* @param outputSchema - Zod schema or JSON-schema response format.
|
|
161
|
+
* @param options - Same options as `withStructuredOutput` (e.g. `{ name }`).
|
|
162
|
+
* @param config - Optional runtime model config overrides.
|
|
163
|
+
*/
|
|
164
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- mirrors ChatOpenAI.withStructuredOutput's constraint; `unknown` rejects zod-inferred interface types
|
|
165
|
+
export function createStructuredModel(agent, outputSchema, options, config) {
|
|
166
|
+
const primary = createModel(agent, config).withStructuredOutput(outputSchema, options);
|
|
167
|
+
const fallbackModel = createFallbackModel(agent, config);
|
|
168
|
+
const fallback = fallbackModel?.withStructuredOutput(outputSchema, options);
|
|
169
|
+
return withResilience(primary, fallback);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Creates a plain-completion model with runnable-level retry and cross-model
|
|
173
|
+
* fallback, for call sites that `invoke()` the model directly (no
|
|
174
|
+
* `withStructuredOutput`/`bindTools`/`stream` chaining).
|
|
175
|
+
*
|
|
176
|
+
* @param agent - Key identifying which agent's model settings to use.
|
|
177
|
+
* @param config - Optional runtime model config overrides.
|
|
178
|
+
*/
|
|
179
|
+
export function createResilientModel(agent, config) {
|
|
180
|
+
return withResilience(createModel(agent, config), createFallbackModel(agent, config));
|
|
181
|
+
}
|
|
85
182
|
//# sourceMappingURL=model.config.js.map
|