@falai/agent 4.0.0-alpha.7 → 4.0.0-alpha.9
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/cjs/core/FlowSpec.d.ts.map +1 -1
- package/dist/cjs/core/FlowSpec.js +22 -2
- package/dist/cjs/core/FlowSpec.js.map +1 -1
- package/dist/cjs/core/Runner.d.ts +5 -0
- package/dist/cjs/core/Runner.d.ts.map +1 -1
- package/dist/cjs/core/Runner.js +9 -3
- package/dist/cjs/core/Runner.js.map +1 -1
- package/dist/cjs/core/Understand.d.ts.map +1 -1
- package/dist/cjs/core/Understand.js +4 -6
- package/dist/cjs/core/Understand.js.map +1 -1
- package/dist/cjs/types/agent.d.ts +5 -1
- package/dist/cjs/types/agent.d.ts.map +1 -1
- package/dist/core/FlowSpec.d.ts.map +1 -1
- package/dist/core/FlowSpec.js +22 -2
- package/dist/core/FlowSpec.js.map +1 -1
- package/dist/core/Runner.d.ts +5 -0
- package/dist/core/Runner.d.ts.map +1 -1
- package/dist/core/Runner.js +9 -3
- package/dist/core/Runner.js.map +1 -1
- package/dist/core/Understand.d.ts.map +1 -1
- package/dist/core/Understand.js +4 -6
- package/dist/core/Understand.js.map +1 -1
- package/dist/types/agent.d.ts +5 -1
- package/dist/types/agent.d.ts.map +1 -1
- package/docs/concepts/architecture.md +1 -1
- package/docs/concepts/pipeline.md +4 -4
- package/docs/guides/conditions.md +1 -1
- package/docs/guides/triggers.md +2 -2
- package/docs/reference/agent.md +2 -2
- package/docs/reference/flow-spec.md +1 -1
- package/docs/reference/trigger.md +2 -2
- package/docs/rfc/v4-one-flow.md +2 -2
- package/package.json +1 -1
- package/src/core/FlowSpec.ts +24 -6
- package/src/core/Runner.ts +10 -3
- package/src/core/Understand.ts +3 -6
- package/src/types/agent.ts +5 -1
package/src/core/FlowSpec.ts
CHANGED
|
@@ -410,11 +410,8 @@ export function validateFlow<C = unknown, D = LooseData>(
|
|
|
410
410
|
continue;
|
|
411
411
|
}
|
|
412
412
|
if (!matchesParam(def, value)) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
`parameter "${param}" of action "${name}" must be ${describeDef(def)}, got ${describe(value)}`,
|
|
416
|
-
"Values are not coerced; write the right type.",
|
|
417
|
-
);
|
|
413
|
+
const { expected, got, fix } = mismatch(def, value);
|
|
414
|
+
throw problem(at, `parameter "${param}" of action "${name}" must be ${expected}, got ${got}`, fix);
|
|
418
415
|
}
|
|
419
416
|
}
|
|
420
417
|
for (const param of Object.keys(given)) {
|
|
@@ -554,12 +551,33 @@ function matches(def: ScalarDef, value: unknown): boolean {
|
|
|
554
551
|
return typeof value !== "boolean" && def.enum.includes(value);
|
|
555
552
|
}
|
|
556
553
|
|
|
554
|
+
/**
|
|
555
|
+
* What a rejected value should have been and what it was. A value of the right type that is
|
|
556
|
+
* not a listed one names the listed values: "must be a string, got string" would say nothing.
|
|
557
|
+
*/
|
|
558
|
+
function mismatch(def: ParamDef, value: unknown): { expected: string; got: string; fix: string } {
|
|
559
|
+
const scalar = def.type === "array" ? def.items : def;
|
|
560
|
+
const { enum: allowed, ...typeOnly } = scalar;
|
|
561
|
+
const items = def.type === "array" && Array.isArray(value) ? value : [value];
|
|
562
|
+
const off = allowed ? items.findIndex((item) => matches(typeOnly, item) && !matches(scalar, item)) : -1;
|
|
563
|
+
if (!allowed || off === -1) {
|
|
564
|
+
return { expected: describeDef(def), got: describe(value), fix: "Values are not coerced; write the right type." };
|
|
565
|
+
}
|
|
566
|
+
const list = (v: unknown) => JSON.stringify(v);
|
|
567
|
+
return { expected: `one of ${allowed.map(list).join(", ")}`, got: list(items[off]), fix: "Use one of the listed values." };
|
|
568
|
+
}
|
|
569
|
+
|
|
557
570
|
function describe(value: unknown): string {
|
|
558
571
|
return Array.isArray(value) ? "list" : value === null ? "null" : typeof value;
|
|
559
572
|
}
|
|
560
573
|
|
|
561
574
|
function describeDef(def: ParamDef): string {
|
|
562
|
-
return def.type === "array" ? `a list of ${def.items.type}` :
|
|
575
|
+
return def.type === "array" ? `a list of ${def.items.type}s` : `${article(def.type)} ${def.type}`;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/** "an integer", "a string". A vowel test rather than one hard-coded type, so a new type reads right for free. */
|
|
579
|
+
function article(type: string): string {
|
|
580
|
+
return /^[aeiou]/.test(type) ? "an" : "a";
|
|
563
581
|
}
|
|
564
582
|
|
|
565
583
|
// ── flowSpecSchema ──────────────────────────────────────────────────────
|
package/src/core/Runner.ts
CHANGED
|
@@ -508,8 +508,7 @@ export class Runner<C = unknown, D = unknown> {
|
|
|
508
508
|
const floorRun = this.floorRun(turn);
|
|
509
509
|
const floorFlow = floorRun && this.flows.get(floorRun.flowId);
|
|
510
510
|
const eligible = this.eligibleMessageFlows(turn);
|
|
511
|
-
|
|
512
|
-
const messageFlows = !floorRun && eligible.length === 1 ? [] : eligible;
|
|
511
|
+
const messageFlows = !floorRun && this.startsUnscored(turn, eligible) ? [] : eligible;
|
|
513
512
|
const mentionFlows = this.mentionFlows()
|
|
514
513
|
.filter(({ flow, trigger }) => trigger.mention.length > 0 && this.repeatAllows(turn, flow, trigger, turn.triggerKey))
|
|
515
514
|
.map(({ flow }) => flow);
|
|
@@ -562,6 +561,14 @@ export class Runner<C = unknown, D = unknown> {
|
|
|
562
561
|
return this.messageFlows(turn, (list) => list.length > 0);
|
|
563
562
|
}
|
|
564
563
|
|
|
564
|
+
/**
|
|
565
|
+
* The lone eligible flow starts without a score only when a low score would have nowhere
|
|
566
|
+
* else to send the message: no catch-all passes and the idle speaker is silent (S9).
|
|
567
|
+
*/
|
|
568
|
+
private startsUnscored(turn: Turn<C, D>, eligible: Flow<C, D>[]): boolean {
|
|
569
|
+
return eligible.length === 1 && this.options.idle === "silent" && this.messageFlows(turn, (list) => list.length === 0).length === 0;
|
|
570
|
+
}
|
|
571
|
+
|
|
565
572
|
private messageFlows(turn: Turn<C, D>, accept: (list: string[]) => boolean): Flow<C, D>[] {
|
|
566
573
|
const out: Flow<C, D>[] = [];
|
|
567
574
|
for (const flow of this.flows.values()) {
|
|
@@ -605,7 +612,7 @@ export class Runner<C = unknown, D = unknown> {
|
|
|
605
612
|
const current = scores[asker.flowId] ?? 0;
|
|
606
613
|
const top = best(eligible.filter((f) => f.id !== asker.flowId));
|
|
607
614
|
if (top && top.score >= current + ROUTE_STICKY && top.score >= ROUTE_MIN) route = top.flow;
|
|
608
|
-
} else if (
|
|
615
|
+
} else if (this.startsUnscored(turn, eligible)) {
|
|
609
616
|
route = eligible[0];
|
|
610
617
|
} else {
|
|
611
618
|
const top = best(eligible);
|
package/src/core/Understand.ts
CHANGED
|
@@ -44,12 +44,9 @@ export class Understand<C = unknown, D = unknown> {
|
|
|
44
44
|
const candidates = candidateFlows(req);
|
|
45
45
|
const onlyRouting =
|
|
46
46
|
req.mentionFlows.length === 0 && req.branches.length === 0 && Object.keys(req.fields).length === 0;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const only = candidates[0];
|
|
51
|
-
return only && !req.floor ? { ...empty(0), flows: { [only.id]: 100 } } : empty(0);
|
|
52
|
-
}
|
|
47
|
+
// Nothing to compare or extract: no candidates, or only the floor's own flow. A lone
|
|
48
|
+
// candidate with nobody on the floor is here because the runner wants it scored.
|
|
49
|
+
if (onlyRouting && candidates.length <= (req.floor ? 1 : 0)) return empty(0);
|
|
53
50
|
|
|
54
51
|
const aliases = new Aliases();
|
|
55
52
|
const jsonSchema = buildEnvelope(req, candidates, aliases);
|
package/src/types/agent.ts
CHANGED
|
@@ -87,7 +87,11 @@ export type TurnBase<C = unknown, D = unknown> = ContextField<C> & {
|
|
|
87
87
|
sessionId: string;
|
|
88
88
|
/** Absent on a first turn. A wake never creates a session. */
|
|
89
89
|
session?: Session<D>;
|
|
90
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* The conversation BEFORE this input. Pass it on every input kind, wakes included.
|
|
92
|
+
* Leave out the message this turn carries: both calls quote it on their own, so a
|
|
93
|
+
* history that ends with it makes the model read it twice.
|
|
94
|
+
*/
|
|
91
95
|
history?: History;
|
|
92
96
|
silenced?: Silenced;
|
|
93
97
|
/** Host anchors this session belongs to, e.g. `{ lead: { key: 'lead:456', lastInboundAt } }`. */
|