@kuralle-agents/core 0.3.11 → 0.3.12

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.
@@ -89,6 +89,7 @@ export class Runtime {
89
89
  steps,
90
90
  toolExecutor,
91
91
  model,
92
+ controlModel: opened.agent.controlModel ?? model,
92
93
  abortSignal: abortController.signal,
93
94
  emit,
94
95
  refinementPolicies: policies.refinementPolicies,
@@ -109,8 +109,7 @@ export class TextDriver {
109
109
  // voice are identical). The model's prose is discarded; the user-facing
110
110
  // question is emitted deterministically by the flow engine (CollectNode.ask).
111
111
  runExtraction(node, ctx) {
112
- const model = node.node.model ?? ctx.model;
113
- return runSilentExtraction(node, ctx, model, resolveMaxSteps(ctx.limits, this.maxSteps));
112
+ return runSilentExtraction(node, ctx, ctx.controlModel, resolveMaxSteps(ctx.limits, this.maxSteps));
114
113
  }
115
114
  async runStructured(node, ctx) {
116
115
  const base = composeSystem(ctx.baseInstructions, resolveInstructions(node.instructions, ctx.runState.state), ctx.runState.state);
@@ -125,10 +124,11 @@ export class TextDriver {
125
124
  .join(', ')}. Respond with only the chosen id, nothing else.`
126
125
  : base;
127
126
  const { object } = await generateObject({
128
- model: ctx.model,
127
+ model: ctx.controlModel,
129
128
  schema,
130
129
  system,
131
130
  messages: ctx.runState.messages,
131
+ temperature: 0,
132
132
  abortSignal: ctx.abortSignal,
133
133
  });
134
134
  return object;
@@ -75,10 +75,11 @@ export class VoiceDriver {
75
75
  .join(', ')}. Respond with only the chosen id, nothing else.`
76
76
  : base;
77
77
  const { object } = await generateObject({
78
- model: ctx.model,
78
+ model: ctx.controlModel,
79
79
  schema,
80
80
  system,
81
81
  messages: ctx.runState.messages,
82
+ temperature: 0,
82
83
  abortSignal: ctx.abortSignal,
83
84
  });
84
85
  return object;
@@ -89,7 +90,7 @@ export class VoiceDriver {
89
90
  // behavior to TextDriver — voice and text emit the same structural events; the
90
91
  // user-facing question is the deterministic CollectNode.ask, synthesized after.
91
92
  runExtraction(node, ctx) {
92
- return runSilentExtraction(node, ctx, ctx.model, resolveMaxSteps(ctx.limits, this.maxSteps));
93
+ return runSilentExtraction(node, ctx, ctx.controlModel, resolveMaxSteps(ctx.limits, this.maxSteps));
93
94
  }
94
95
  async awaitUser(ctx) {
95
96
  if (this.pendingBargeInInput != null) {
@@ -20,7 +20,14 @@ export async function runSilentExtraction(node, ctx, model, maxSteps) {
20
20
  const aiTools = resolveExtractionTools(node);
21
21
  const out = { text: '', toolResults: [] };
22
22
  for (let step = 0; step < maxSteps; step += 1) {
23
- const result = streamText({ model, system, messages, tools: aiTools, abortSignal: ctx.abortSignal });
23
+ const result = streamText({
24
+ model,
25
+ system,
26
+ messages,
27
+ tools: aiTools,
28
+ temperature: 0,
29
+ abortSignal: ctx.abortSignal,
30
+ });
24
31
  for await (const part of result.fullStream) {
25
32
  // Intentionally NOT handling 'text-delta' — extraction never speaks.
26
33
  if (part.type === 'error') {
@@ -20,6 +20,7 @@ export interface CtxDeps {
20
20
  toolExecutor: EffectToolExecutor;
21
21
  hookRunner?: HookRunner;
22
22
  model: LanguageModel;
23
+ controlModel?: LanguageModel;
23
24
  refinementPolicies?: RefinementCapability[];
24
25
  validationPolicies?: ValidationCapability[];
25
26
  inputProcessors?: InputProcessor[];
@@ -99,6 +99,7 @@ function makeCtx(deps) {
99
99
  toolExecutor: deps.toolExecutor,
100
100
  hookRunner: deps.hookRunner ?? {},
101
101
  model: deps.model,
102
+ controlModel: deps.controlModel ?? deps.model,
102
103
  refinementPolicies: deps.refinementPolicies ?? [],
103
104
  validationPolicies: deps.validationPolicies ?? [],
104
105
  inputProcessors: deps.inputProcessors ?? [],
@@ -21,7 +21,7 @@ export async function hostLoop(options) {
21
21
  const selection = await select({
22
22
  agent,
23
23
  run,
24
- model: agent.routing?.model ?? agent.model ?? ctx.model,
24
+ model: agent.routing?.model ?? ctx.controlModel,
25
25
  alwaysRoute,
26
26
  });
27
27
  if (selection.kind === 'enterFlow') {
@@ -33,6 +33,7 @@ export async function selectHostTarget(options) {
33
33
  const { object } = await generateObject({
34
34
  model,
35
35
  schema: selectionSchema,
36
+ temperature: 0,
36
37
  system: 'You are an internal routing classifier. Choose exactly one action. ' +
37
38
  'Output schema fields only — never user-facing prose. ' +
38
39
  'Prefer route when a route condition clearly matches; else enterFlow when a flow description matches an uncompleted flow; else keep. Never re-enter a completed flow.',
@@ -14,6 +14,10 @@ export interface AgentConfig {
14
14
  description?: string;
15
15
  instructions?: Instructions;
16
16
  model?: LanguageModel;
17
+ /** Optional model for the control path (routing, decide, extraction), run at
18
+ * temperature 0 for determinism. Defaults to `model` (the speaker) when unset.
19
+ * Set this to pin control to a reliable provider independent of the speaker. */
20
+ controlModel?: LanguageModel;
17
21
  tools?: ToolSet;
18
22
  effectTools?: Record<string, AnyTool>;
19
23
  /** Safe, always-available tools made model-visible in EVERY speaking node turn
@@ -51,6 +51,8 @@ export interface RunContext {
51
51
  toolExecutor: EffectToolExecutor;
52
52
  hookRunner: HookRunner;
53
53
  model: LanguageModel;
54
+ /** Control-path model (routing, decide, extraction) at temperature 0. */
55
+ controlModel: LanguageModel;
54
56
  refinementPolicies: RefinementCapability[];
55
57
  validationPolicies: ValidationCapability[];
56
58
  inputProcessors: InputProcessor[];
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "git+https://github.com/kuralle/kuralle-agents.git",
7
7
  "directory": "packages/kuralle-core"
8
8
  },
9
- "version": "0.3.11",
9
+ "version": "0.3.12",
10
10
  "description": "A framework for structured conversational AI agents",
11
11
  "publishConfig": {
12
12
  "access": "public"
@@ -97,7 +97,7 @@
97
97
  "dotenv": "^16.4.0",
98
98
  "typescript": "^5.3.0",
99
99
  "zod": "^3.23.0",
100
- "@kuralle-agents/realtime-audio": "0.3.11"
100
+ "@kuralle-agents/realtime-audio": "0.3.12"
101
101
  },
102
102
  "dependencies": {
103
103
  "chrono-node": "^2.6.0",