jsfe 1.14.2 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/host/intent-stages.d.ts +16 -10
- package/dist/host/intent-stages.d.ts.map +1 -1
- package/dist/host/intent-stages.js +45 -29
- package/dist/runtime-identity.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -354,7 +354,10 @@ The host runs either, or both as a hedge, and replies with the canonical JSON: `
|
|
|
354
354
|
stage 1, `{ "parameters": { … } }` for stage 2. `jsfe/host` exports `buildFlowStage`, `buildParameterStage`
|
|
355
355
|
and the decoders (`decodeFlowLlm`, `decodeFlowSystemOne`, `decodeParametersLlm`, `decodeParametersSystemOne`,
|
|
356
356
|
`isNearMiss`), so a host never interprets an answer itself. Only primaries are ever activated by detection;
|
|
357
|
-
a stage-2 failure starts the flow without its parameters (it asks for them).
|
|
357
|
+
a stage-2 failure starts the flow without its parameters (it asks for them). Stage 1's System One request
|
|
358
|
+
is one `choice` over the primaries (`route`) and one `noul` (`any`): does the message start any of them; the
|
|
359
|
+
routed flow starts when `any` reaches 0.5, else `"None"`. Both executors are told that a question starts a flow
|
|
360
|
+
only when it asks for the flow's action, never because it mentions its subject. The default, `'single-call'`,
|
|
358
361
|
is the original single call.
|
|
359
362
|
|
|
360
363
|
**The reply ranker (contextual DISPATCH, `onResponse: "continue"`):** when a flow's question gets a reply its
|
|
@@ -23,8 +23,6 @@
|
|
|
23
23
|
import type { FlowParameterDefinition } from '../index.js';
|
|
24
24
|
/** A noul at or above this is a yes. */
|
|
25
25
|
export declare const STAGE_YES_THRESHOLD = 0.5;
|
|
26
|
-
/** Stage 1, System One: two top flows that together reach this pick the higher one (prod 10-17). */
|
|
27
|
-
export declare const STAGE_PAIR_SUM = 0.7;
|
|
28
26
|
export interface StageTurn {
|
|
29
27
|
role: 'user' | 'assistant';
|
|
30
28
|
content: string;
|
|
@@ -52,9 +50,9 @@ export interface StageSystemOne<I> {
|
|
|
52
50
|
questions: Record<string, unknown>;
|
|
53
51
|
index: I[];
|
|
54
52
|
}
|
|
53
|
+
/** Stage 1, System One: `route` is the choice among the flows, `any` whether the message starts one at all. */
|
|
55
54
|
export interface FlowQuestionRef {
|
|
56
|
-
id:
|
|
57
|
-
flow: number;
|
|
55
|
+
id: 'route' | 'any';
|
|
58
56
|
}
|
|
59
57
|
export type ParameterQuestionRef = {
|
|
60
58
|
id: string;
|
|
@@ -99,26 +97,34 @@ export type StageSystemOneAnswers = Record<string, {
|
|
|
99
97
|
} | undefined> | undefined;
|
|
100
98
|
/** The flow's own parameters, never asked of the user. */
|
|
101
99
|
export declare const isInternalParameter: (p: StageParameter | FlowParameterDefinition) => boolean;
|
|
102
|
-
/**
|
|
100
|
+
/**
|
|
101
|
+
* Stage 1's instructions for both executors. `flows` are the primaries offered.
|
|
102
|
+
*
|
|
103
|
+
* System One gets ONE request with two questions over the same list: `route`, a `choice` among the flows (each
|
|
104
|
+
* option's criterion is its description, so every flow is weighed against the others), and `any`, a `noul`
|
|
105
|
+
* asking whether the message starts any of them. TypeSafe documents that a "none of the above" option may not
|
|
106
|
+
* work well and advises the companion noul.
|
|
107
|
+
*/
|
|
103
108
|
export declare function buildFlowStage(input: string, conversation: StageTurn[], flows: StageFlow[]): FlowStageRequest;
|
|
104
109
|
/** A text model's stage-1 answer as the canonical reply; throws when it names no offered flow. */
|
|
105
110
|
export declare function decodeFlowLlm(text: string, stage: FlowStageRequest): {
|
|
106
111
|
flowName: string;
|
|
107
112
|
};
|
|
108
113
|
/**
|
|
109
|
-
* System One's stage-1 answers as the canonical reply
|
|
110
|
-
*
|
|
114
|
+
* System One's stage-1 answers as the canonical reply. `any` at or above the threshold starts the flow `route`
|
|
115
|
+
* chose; below it, "None". `scores` are the top three flows by the chance that each is the one started,
|
|
116
|
+
* any × its route probability (for logs and the host's near-miss hedge); `any` is logged with them. Throws when
|
|
117
|
+
* either answer is missing or names no offered flow.
|
|
111
118
|
*/
|
|
112
|
-
export declare function decodeFlowSystemOne(answers: StageSystemOneAnswers, stage: FlowStageRequest, { threshold
|
|
119
|
+
export declare function decodeFlowSystemOne(answers: StageSystemOneAnswers, stage: FlowStageRequest, { threshold }?: {
|
|
113
120
|
threshold?: number | undefined;
|
|
114
|
-
pairSum?: number | undefined;
|
|
115
121
|
}): {
|
|
116
122
|
flowName: string;
|
|
117
123
|
scores: {
|
|
118
124
|
name: string;
|
|
119
125
|
p: number;
|
|
120
126
|
}[];
|
|
121
|
-
|
|
127
|
+
any: number;
|
|
122
128
|
};
|
|
123
129
|
/**
|
|
124
130
|
* A System One "None" that is a near miss: the chance that at least one of its two top flows applies,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent-stages.d.ts","sourceRoot":"","sources":["../../src/host/intent-stages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAE3D,wCAAwC;AACxC,eAAO,MAAM,mBAAmB,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"intent-stages.d.ts","sourceRoot":"","sources":["../../src/host/intent-stages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAE3D,wCAAwC;AACxC,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,MAAM,WAAW,SAAS;IAAG,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AAC1E,MAAM,WAAW,SAAS;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE;AAC5E,MAAM,WAAW,cAAc;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE;AAChK,MAAM,WAAW,QAAQ;IAAG,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE;AAC3F,MAAM,WAAW,cAAc,CAAC,CAAC;IAAI,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,KAAK,EAAE,CAAC,EAAE,CAAA;CAAE;AACrH,+GAA+G;AAC/G,MAAM,WAAW,eAAe;IAAG,EAAE,EAAE,OAAO,GAAG,KAAK,CAAA;CAAE;AACxD,MAAM,MAAM,oBAAoB,GAC5B;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,aAAa,CAAC;IACpB,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,SAAS,EAAE,CAAC;IAC1B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,GAAG,EAAE,QAAQ,CAAC;IACd,SAAS,EAAE,cAAc,CAAC,eAAe,CAAC,CAAC;CAC5C;AACD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,oBAAoB,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,SAAS,EAAE,CAAC;IAC1B,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,cAAc,EAAE,CAAA;KAAE,CAAC;IACjG,GAAG,EAAE,QAAQ,CAAC;IACd,+DAA+D;IAC/D,SAAS,EAAE,cAAc,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAAC;IACvD,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AACD,MAAM,MAAM,kBAAkB,GAAG,gBAAgB,GAAG,qBAAqB,CAAC;AAC1E,kDAAkD;AAClD,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;AAoB/F,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,GAAI,GAAG,cAAc,GAAG,uBAAuB,YACiB,CAAC;AAajG;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,gBAAgB,CAoC7G;AAED,kGAAkG;AAClG,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAUzF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,qBAAqB,EAC9B,KAAK,EAAE,gBAAgB,EACvB,EAAE,SAA+B,EAAE;;CAAK,GACvC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAa1E;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE;IAAE,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,EAAE,SAAS,GAAE,MAA4B,GAAG,OAAO,CAGpG;AAGD,uFAAuF;AACvF,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,CAAC,cAAc,GAAG,uBAAuB,CAAC,EAAE,GAAG,SAAS,GAAG,OAAO,CAEhH;AAED,wEAAwE;AACxE,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,SAAS,EAAE,EACzB,IAAI,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,CAAC,cAAc,GAAG,uBAAuB,CAAC,EAAE,CAAA;CAAE,GAC5H,qBAAqB,CA+CvB;AAED,uHAAuH;AACvH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAYvH;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,qBAAqB,EAC9B,KAAK,EAAE,qBAAqB,EAC5B,EAAE,SAA+B,EAAE;;CAAK,GACvC;IAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAkBzC;AAED,0FAA0F;AAC1F,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,kBAAkB,CAGpF"}
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
*/
|
|
23
23
|
/** A noul at or above this is a yes. */
|
|
24
24
|
export const STAGE_YES_THRESHOLD = 0.5;
|
|
25
|
-
/** Stage 1, System One: two top flows that together reach this pick the higher one (prod 10-17). */
|
|
26
|
-
export const STAGE_PAIR_SUM = 0.7;
|
|
27
25
|
// ── the rules: written once, rendered for both executors ─────────────────────────────────────────────
|
|
28
26
|
const FLOW_TRUE = "The latest message, read with the conversation, is what the workflow's description says should start it: the user asks for its action or information, or raises the problem it exists to handle. Also when it accepts the assistant's offer in the conversation to start or resume it (assistant: \"Shall I try the payment again?\" → user: \"yes\").";
|
|
29
27
|
const FLOW_FALSE = "It only shares a topic with the workflow, is not what the workflow's description covers, or is a greeting, an acknowledgement or a fragment that asks for nothing.";
|
|
28
|
+
/** A question usually asks for information the assistant gives itself; it is weighed before it is taken as a trigger. */
|
|
29
|
+
const QUESTION_RULE = "A question usually asks for information the assistant can give itself. Consider it carefully: it starts a workflow only when it asks for that workflow's action or raises the problem it exists to handle, never because it mentions the workflow's subject (\"can I pay with this card at a store?\" asks how the card is used, not to make a payment).";
|
|
30
30
|
const PARAMETER_RULES = [
|
|
31
31
|
'Give a value only when the user gave it, in the latest message or earlier in the conversation. Never guess.',
|
|
32
32
|
"Write it as the parameter's rule says; otherwise the value alone, in the user's words (never the whole sentence around it).",
|
|
@@ -49,33 +49,45 @@ const stateOf = (conversation, input, instructions) => ({
|
|
|
49
49
|
});
|
|
50
50
|
const jsonTail = (schema) => `\n\n<json-schema>\n${JSON.stringify(schema)}\n</json-schema>\n\nRespond ONLY with valid JSON matching the schema. No other text.`;
|
|
51
51
|
// ── stage 1 ───────────────────────────────────────────────────────────────────────────────────────────
|
|
52
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Stage 1's instructions for both executors. `flows` are the primaries offered.
|
|
54
|
+
*
|
|
55
|
+
* System One gets ONE request with two questions over the same list: `route`, a `choice` among the flows (each
|
|
56
|
+
* option's criterion is its description, so every flow is weighed against the others), and `any`, a `noul`
|
|
57
|
+
* asking whether the message starts any of them. TypeSafe documents that a "none of the above" option may not
|
|
58
|
+
* work well and advises the companion noul.
|
|
59
|
+
*/
|
|
53
60
|
export function buildFlowStage(input, conversation, flows) {
|
|
54
61
|
const names = flows.map(f => f.name);
|
|
55
62
|
const schema = {
|
|
56
63
|
type: 'object', additionalProperties: false, required: ['flowName'],
|
|
57
64
|
properties: { flowName: { type: 'string', enum: [...names, 'None'] } },
|
|
58
65
|
};
|
|
66
|
+
const list = flows.map(f => `- ${f.name}: ${f.description}`);
|
|
59
67
|
const system = [
|
|
60
68
|
'Decide which workflow, if any, the latest message starts.',
|
|
61
69
|
`A workflow is chosen when: ${FLOW_TRUE}`,
|
|
62
70
|
`"None" when: ${FLOW_FALSE}`,
|
|
71
|
+
QUESTION_RULE,
|
|
63
72
|
'Answer with the exact workflow name, or "None".',
|
|
64
73
|
'',
|
|
65
74
|
'<workflows>',
|
|
66
|
-
...
|
|
75
|
+
...list,
|
|
67
76
|
'</workflows>',
|
|
68
77
|
].join('\n') + jsonTail(schema);
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
const workflows = Object.fromEntries(flows.map(f => [f.name, f.description]));
|
|
79
|
+
const questions = {
|
|
80
|
+
any: {
|
|
81
|
+
type: 'noul',
|
|
82
|
+
criteria: { true: `${FLOW_TRUE} The workflow is one of \`workflows\`.`, false: `${FLOW_FALSE} ${QUESTION_RULE}` },
|
|
83
|
+
instructions: { workflows, question: 'Should the latest message start one of the workflows in `workflows`?' },
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
questions.route = {
|
|
87
|
+
type: 'choice', criteria: workflows,
|
|
88
|
+
instructions: 'Which workflow does the latest message, read with the conversation, come closest to starting?',
|
|
89
|
+
};
|
|
90
|
+
const index = [{ id: 'any' }, { id: 'route' }];
|
|
79
91
|
return {
|
|
80
92
|
task: 'detect_flow', stage: 1, input, conversation, flows,
|
|
81
93
|
llm: { system, user: conversationText(conversation, input), schema },
|
|
@@ -97,23 +109,27 @@ export function decodeFlowLlm(text, stage) {
|
|
|
97
109
|
}
|
|
98
110
|
}
|
|
99
111
|
/**
|
|
100
|
-
* System One's stage-1 answers as the canonical reply
|
|
101
|
-
*
|
|
112
|
+
* System One's stage-1 answers as the canonical reply. `any` at or above the threshold starts the flow `route`
|
|
113
|
+
* chose; below it, "None". `scores` are the top three flows by the chance that each is the one started,
|
|
114
|
+
* any × its route probability (for logs and the host's near-miss hedge); `any` is logged with them. Throws when
|
|
115
|
+
* either answer is missing or names no offered flow.
|
|
102
116
|
*/
|
|
103
|
-
export function decodeFlowSystemOne(answers, stage, { threshold = STAGE_YES_THRESHOLD
|
|
104
|
-
const
|
|
105
|
-
|
|
117
|
+
export function decodeFlowSystemOne(answers, stage, { threshold = STAGE_YES_THRESHOLD } = {}) {
|
|
118
|
+
const any = Number(answers?.any?.noul);
|
|
119
|
+
if (!Number.isFinite(any))
|
|
120
|
+
throw new Error('System One: question any unanswered');
|
|
121
|
+
const names = stage.flows.map(f => f.name);
|
|
122
|
+
const route = answers?.route;
|
|
123
|
+
const raw = route?.probabilities;
|
|
124
|
+
if (!raw || typeof raw !== 'object')
|
|
125
|
+
throw new Error('System One: question route unanswered');
|
|
126
|
+
if (!names.includes(String(route?.choice)))
|
|
127
|
+
throw new Error(`System One: route chose "${String(route?.choice).slice(0, 60)}", not an offered workflow`);
|
|
128
|
+
const probabilities = Object.fromEntries(names.map(n => [n, Number(raw[n]) || 0]));
|
|
129
|
+
const ranked = names.map(n => ({ name: n, p: any * probabilities[n] })).sort((a, b) => b.p - a.p);
|
|
106
130
|
const scores = ranked.slice(0, 3);
|
|
107
|
-
const [
|
|
108
|
-
|
|
109
|
-
return { flowName: a.name, scores, pair: false };
|
|
110
|
-
}
|
|
111
|
-
else if (a && b && a.p + b.p >= pairSum) {
|
|
112
|
-
return { flowName: a.name, scores, pair: true };
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
return { flowName: 'None', scores, pair: false };
|
|
116
|
-
}
|
|
131
|
+
const flowName = any >= threshold && ranked[0] ? ranked[0].name : 'None';
|
|
132
|
+
return { flowName, scores, any };
|
|
117
133
|
}
|
|
118
134
|
/**
|
|
119
135
|
* A System One "None" that is a near miss: the chance that at least one of its two top flows applies,
|
package/dist/runtime-identity.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const JSFE_RUNTIME_FINGERPRINT = "sha256:
|
|
1
|
+
export const JSFE_RUNTIME_FINGERPRINT = "sha256:bbc5a083f51750e417f571d17b1d5880f511fc5506a76f235c6740e55dad7640";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsfe",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"description": "TypeScript workflow engine for conversational/automation apps: multi-flow orchestration, interrupt/resume, stack-based control, robust errors, and flexible tool/REST integration.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ron Pinkas",
|