@zhanla/sdk-ts 0.2.0 → 0.3.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/bin/discover.js CHANGED
@@ -11,6 +11,7 @@
11
11
 
12
12
  import { pathToFileURL } from "url";
13
13
  import { resolve } from "path";
14
+ import { loadDotenvLocal, splitFileSpec } from "./env.js";
14
15
 
15
16
  function isComponentLike(value) {
16
17
  return (
@@ -77,8 +78,34 @@ function stableSerialize(value) {
77
78
  return JSON.stringify(value);
78
79
  }
79
80
 
80
- function sortRefsByKey(refs) {
81
- return [...refs].sort((left, right) => left.key.localeCompare(right.key));
81
+ function projectRefForHash(ref) {
82
+ return { key: ref.key, component_type: ref.component_type };
83
+ }
84
+
85
+ function sortAndProjectRefs(refs) {
86
+ return [...refs].map(projectRefForHash).sort((a, b) => a.key.localeCompare(b.key));
87
+ }
88
+
89
+ function normalizeStepForHash(step) {
90
+ if (step.is_conditional) return step;
91
+ const ref = step.component_ref;
92
+ if (ref == null) return step;
93
+ return { ...step, component_ref: projectRefForHash(ref) };
94
+ }
95
+
96
+ function normalizeEvalNodeForHash(node) {
97
+ if (node == null || typeof node !== "object") return node;
98
+ const result = { ...node };
99
+ if (result.eval_ref != null) result.eval_ref = projectRefForHash(result.eval_ref);
100
+ for (const branch of ["if_pass", "if_fail"]) {
101
+ if (Array.isArray(result[branch])) {
102
+ result[branch] = result[branch].map((edge) => {
103
+ if (edge == null || typeof edge !== "object" || edge.node == null) return edge;
104
+ return { ...edge, node: normalizeEvalNodeForHash(edge.node) };
105
+ });
106
+ }
107
+ }
108
+ return result;
82
109
  }
83
110
 
84
111
  function behaviorFields(manifest) {
@@ -91,13 +118,13 @@ function behaviorFields(manifest) {
91
118
  if (manifest.model != null) fields.model = manifest.model;
92
119
  if (manifest.temperature != null) fields.temperature = manifest.temperature;
93
120
  if (manifest.output_schema != null) fields.output_schema = manifest.output_schema;
94
- if (manifest.tool_refs != null) fields.tool_refs = sortRefsByKey(manifest.tool_refs);
95
- if (manifest.skill_refs != null) fields.skill_refs = sortRefsByKey(manifest.skill_refs);
96
- if (manifest.agent_refs != null) fields.agent_refs = sortRefsByKey(manifest.agent_refs);
97
- if (manifest.steps != null) fields.steps = manifest.steps;
98
- if (manifest.eval_refs != null) fields.eval_refs = sortRefsByKey(manifest.eval_refs);
121
+ if (manifest.tool_refs != null) fields.tool_refs = sortAndProjectRefs(manifest.tool_refs);
122
+ if (manifest.skill_refs != null) fields.skill_refs = sortAndProjectRefs(manifest.skill_refs);
123
+ if (manifest.agent_refs != null) fields.agent_refs = sortAndProjectRefs(manifest.agent_refs);
124
+ if (manifest.steps != null) fields.steps = manifest.steps.map(normalizeStepForHash);
125
+ if (manifest.eval_refs != null) fields.eval_refs = sortAndProjectRefs(manifest.eval_refs);
99
126
  if (manifest.weights != null) fields.weights = manifest.weights;
100
- if (manifest.root != null) fields.root = manifest.root;
127
+ if (manifest.root != null) fields.root = normalizeEvalNodeForHash(manifest.root);
101
128
  if (manifest.source_code != null) fields.source_code = manifest.source_code;
102
129
  if (manifest.model_response_format != null) {
103
130
  fields.model_response_format = manifest.model_response_format;
@@ -186,17 +213,10 @@ export function buildDiscoveredManifests({
186
213
  }
187
214
 
188
215
  export async function runDiscover(fileSpec) {
189
- let filePath = fileSpec;
190
- let symbolName = null;
191
-
192
- const colonIndex = fileSpec.lastIndexOf(":");
193
- // Handle Windows drive letters (e.g. C:\...) — only split if colon is not at position 1
194
- if (colonIndex > 1) {
195
- filePath = fileSpec.slice(0, colonIndex);
196
- symbolName = fileSpec.slice(colonIndex + 1) || null;
197
- }
216
+ const { filePath, symbolName } = splitFileSpec(fileSpec);
198
217
 
199
218
  const absolutePath = resolve(filePath);
219
+ loadDotenvLocal(absolutePath);
200
220
  const fileUrl = pathToFileURL(absolutePath).href;
201
221
 
202
222
  let moduleExports;
package/bin/env.js ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync, readFileSync, statSync } from "fs";
4
+ import { dirname, join, resolve } from "path";
5
+
6
+ function isDirectory(path) {
7
+ try {
8
+ return statSync(path).isDirectory();
9
+ } catch {
10
+ return false;
11
+ }
12
+ }
13
+
14
+ export function splitFileSpec(fileSpec) {
15
+ const colonIndex = fileSpec.lastIndexOf(":");
16
+ // Preserve Windows drive letters (e.g. C:\...) by only splitting on later colons.
17
+ if (colonIndex > 1) {
18
+ return {
19
+ filePath: fileSpec.slice(0, colonIndex),
20
+ symbolName: fileSpec.slice(colonIndex + 1) || null,
21
+ };
22
+ }
23
+ return { filePath: fileSpec, symbolName: null };
24
+ }
25
+
26
+ export function findDotenvLocal(startPath) {
27
+ let current = resolve(startPath);
28
+ if (!isDirectory(current)) {
29
+ current = dirname(current);
30
+ }
31
+
32
+ while (true) {
33
+ const candidate = join(current, ".env.local");
34
+ if (existsSync(candidate)) {
35
+ return candidate;
36
+ }
37
+ const parent = dirname(current);
38
+ if (parent === current) {
39
+ return null;
40
+ }
41
+ current = parent;
42
+ }
43
+ }
44
+
45
+ export function loadDotenvLocal(startPath) {
46
+ const envPath = findDotenvLocal(startPath);
47
+ if (!envPath) {
48
+ return null;
49
+ }
50
+
51
+ const source = readFileSync(envPath, "utf8");
52
+ for (const line of source.split(/\r?\n/)) {
53
+ const trimmed = line.trim();
54
+ if (!trimmed || trimmed.startsWith("#")) continue;
55
+ const equalsIndex = trimmed.indexOf("=");
56
+ if (equalsIndex === -1) continue;
57
+
58
+ const key = trimmed.slice(0, equalsIndex).trim();
59
+ let value = trimmed.slice(equalsIndex + 1).trim();
60
+ if (
61
+ (value.startsWith('"') && value.endsWith('"'))
62
+ || (value.startsWith("'") && value.endsWith("'"))
63
+ ) {
64
+ value = value.slice(1, -1);
65
+ }
66
+ if (key && process.env[key] === undefined) {
67
+ process.env[key] = value;
68
+ }
69
+ }
70
+
71
+ return envPath;
72
+ }
package/bin/run.js CHANGED
@@ -11,6 +11,7 @@
11
11
  import { pathToFileURL } from "url";
12
12
  import { resolve } from "path";
13
13
  import readline from "readline";
14
+ import { loadDotenvLocal } from "./env.js";
14
15
 
15
16
  function redirectConsoleToStderr() {
16
17
  const stringifyArg = (arg) => {
@@ -54,6 +55,7 @@ async function loadTargetComponent(target, collectExportedComponents) {
54
55
  const { filePath, symbolName } = parseTarget(target);
55
56
 
56
57
  const absolutePath = resolve(filePath);
58
+ loadDotenvLocal(absolutePath);
57
59
  const fileUrl = pathToFileURL(absolutePath).href;
58
60
 
59
61
  let moduleExports;
@@ -2,12 +2,12 @@
2
2
  * Local execution engine for TypeScript SDK components.
3
3
  * Mirrors the Python CLI executor's component-type dispatch semantics.
4
4
  */
5
- import { BaseComponent } from "./types.js";
5
+ import { ZhanlaComponent } from "./types.js";
6
6
  export interface RowResult {
7
7
  status: "ok" | "error";
8
8
  output?: Record<string, unknown>;
9
9
  error?: string;
10
10
  pathTaken?: Record<string, unknown>;
11
11
  }
12
- export declare function executeComponent(comp: BaseComponent, kwargs: Record<string, unknown>): Promise<Record<string, unknown>>;
13
- export declare function executeRow(component: BaseComponent, evalComponent: BaseComponent | null, row: Record<string, unknown>): Promise<RowResult>;
12
+ export declare function executeComponent(comp: ZhanlaComponent, kwargs: Record<string, unknown>): Promise<Record<string, unknown>>;
13
+ export declare function executeRow(component: ZhanlaComponent, evalComponent: ZhanlaComponent | null, row: Record<string, unknown>): Promise<RowResult>;
package/dist/executor.js CHANGED
@@ -13,6 +13,17 @@ const JSON_OUTPUT_ERROR_MARKERS = [
13
13
  "extra data",
14
14
  "unexpected token",
15
15
  ];
16
+ const EXECUTION_MODES = {
17
+ tool: "code",
18
+ code_eval: "code",
19
+ skill: "prompt",
20
+ agent: "prompt",
21
+ llm_processor: "prompt",
22
+ llm_eval: "prompt",
23
+ orchestration: "dag",
24
+ checklist: "composite",
25
+ eval_tree: "composite",
26
+ };
16
27
  function buildComponentFailureOutput(error) {
17
28
  return { [COMPONENT_ERROR_KEY]: error };
18
29
  }
@@ -58,12 +69,6 @@ function stringifyValue(value) {
58
69
  return String(value);
59
70
  }
60
71
  }
61
- function deriveModelInput(row) {
62
- if ("input" in row) {
63
- return stringifyValue(row["input"]);
64
- }
65
- return stringifyValue(row);
66
- }
67
72
  function deriveExpectedOutput(row) {
68
73
  if ("expected_output" in row) {
69
74
  return stringifyValue(row["expected_output"]);
@@ -151,7 +156,14 @@ async function runTool(comp, kwargs) {
151
156
  return { result };
152
157
  }
153
158
  async function runCodeEval(comp, kwargs) {
154
- const { value: result, logs } = await captureConsoleLogs(async () => executeFn(comp.fn, kwargs, comp.isAsync));
159
+ const modelResponse = kwargs["model_response"];
160
+ const expectedResponse = kwargs["expected_response"] ?? null;
161
+ const { value: result, logs } = await captureConsoleLogs(async () => {
162
+ if (comp.isAsync) {
163
+ return await comp.fn(modelResponse, expectedResponse);
164
+ }
165
+ return comp.fn(modelResponse, expectedResponse);
166
+ });
155
167
  if (result !== null && typeof result === "object" && !Array.isArray(result)) {
156
168
  return logs.length > 0
157
169
  ? { ...result, logs }
@@ -289,6 +301,32 @@ async function runLLMProcessor(comp, kwargs) {
289
301
  async function runLLMEval(comp, kwargs) {
290
302
  return runRunnerComponent(comp, kwargs);
291
303
  }
304
+ function componentExecutionMode(component) {
305
+ return EXECUTION_MODES[component.componentType];
306
+ }
307
+ function componentSourceLanguage(component) {
308
+ switch (component.componentType) {
309
+ case "tool":
310
+ case "agent":
311
+ case "llm_processor":
312
+ case "llm_eval":
313
+ return "typescript";
314
+ default:
315
+ return undefined;
316
+ }
317
+ }
318
+ function componentPrompt(component) {
319
+ if ("instructions" in component && typeof component.instructions === "string" && component.instructions !== "") {
320
+ return component.instructions;
321
+ }
322
+ return undefined;
323
+ }
324
+ function componentSourceCode(component) {
325
+ if ("fn" in component && typeof component.fn === "function") {
326
+ return component.fn.toString();
327
+ }
328
+ return undefined;
329
+ }
292
330
  async function runOrchestration(comp, inputData, executedSteps) {
293
331
  // Flatten input into accumulated so steps can access row fields directly.
294
332
  // Each step's output is also merged at the top level AND stored at step.name.
@@ -331,14 +369,45 @@ async function runOrchestration(comp, inputData, executedSteps) {
331
369
  continue;
332
370
  }
333
371
  const stepInput = { ...accumulated };
334
- const stepOutput = await executeComponent(component, accumulated);
372
+ let stepOutput;
373
+ let nestedTrace;
374
+ if (component.componentType === "orchestration") {
375
+ const nestedSteps = [];
376
+ stepOutput = await runOrchestration(component, accumulated, nestedSteps);
377
+ nestedTrace = { kind: "orchestration", steps: nestedSteps };
378
+ }
379
+ else {
380
+ stepOutput = await executeComponent(component, accumulated);
381
+ }
335
382
  if (executedSteps) {
336
- executedSteps.push({
383
+ const stepRecord = {
337
384
  name: step.name,
338
385
  type: component.componentType,
386
+ component_type: component.componentType,
387
+ component_key: component.key,
339
388
  input: stepInput,
340
389
  output: stepOutput,
341
- });
390
+ };
391
+ const executionMode = componentExecutionMode(component);
392
+ if (executionMode !== undefined) {
393
+ stepRecord.execution_mode = executionMode;
394
+ }
395
+ const sourceLanguage = componentSourceLanguage(component);
396
+ if (sourceLanguage !== undefined) {
397
+ stepRecord.source_language = sourceLanguage;
398
+ }
399
+ const prompt = componentPrompt(component);
400
+ if (prompt !== undefined) {
401
+ stepRecord.prompt = prompt;
402
+ }
403
+ const sourceCode = componentSourceCode(component);
404
+ if (sourceCode !== undefined) {
405
+ stepRecord.source_code = sourceCode;
406
+ }
407
+ if (nestedTrace !== undefined) {
408
+ stepRecord.trace = nestedTrace;
409
+ }
410
+ executedSteps.push(stepRecord);
342
411
  }
343
412
  // Merge step output at top level for convenient access in downstream steps
344
413
  Object.assign(accumulated, stepOutput);
@@ -490,9 +559,8 @@ export async function executeRow(component, evalComponent, row) {
490
559
  };
491
560
  }
492
561
  const evalKwargs = {
493
- model_input: deriveModelInput(row),
494
562
  model_response: deriveModelResponse(output),
495
- expected_output: deriveExpectedOutput(row),
563
+ expected_response: deriveExpectedOutput(row) || null,
496
564
  };
497
565
  try {
498
566
  const evalOutput = await executeComponent(evalComponent, evalKwargs);
package/dist/index.d.ts CHANGED
@@ -2,8 +2,8 @@
2
2
  * zhanla-sdk-ts public API.
3
3
  * Import component classes to define zhanla components in TypeScript.
4
4
  */
5
- export { Tool, CodeEval, Skill, Agent, LLMProcessor, LLMEval, Runner, Conditional, Step, Orchestration, Leaf, Edge, Branch, Checklist, EvalTree, BaseComponent, RUNNABLE_TYPES, EVAL_TYPES, } from "./types.js";
6
- export type { ToolOptions, CodeEvalOptions, SkillOptions, AgentOptions, LLMProcessorOptions, LLMEvalOptions, OrchestrationOptions, ChecklistOptions, EvalTreeOptions, StepOptions, ComponentType, JsonSchema, JsonSchemaObject, JsonSchemaArray, JsonSchemaScalar, ToolCall, LLMResponse, RunnerOptions, RunnerCallOptions, RunnerMessage, } from "./types.js";
5
+ export { Tool, CodeEval, Skill, Agent, LLMProcessor, LLMEval, Runner, Conditional, Step, Orchestration, Leaf, Edge, Branch, Checklist, EvalTree, ZhanlaComponent, RUNNABLE_TYPES, EVAL_TYPES, } from "./types.js";
6
+ export type { ComponentValue, ToolOptions, CodeEvalOptions, SkillOptions, AgentOptions, LLMProcessorOptions, LLMEvalOptions, OrchestrationOptions, ChecklistOptions, EvalTreeOptions, StepOptions, ComponentType, JsonSchema, JsonSchemaObject, JsonSchemaArray, JsonSchemaScalar, ToolCall, LLMResponse, RunnerOptions, RunnerCallOptions, RunnerMessage, } from "./types.js";
7
7
  export { toManifest, collectExportedComponents } from "./manifest.js";
8
8
  export type { ComponentManifest, StepManifest, BranchManifest, EdgeManifest, LeafManifest } from "./manifest.js";
9
9
  export { executeComponent, executeRow } from "./executor.js";
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * zhanla-sdk-ts public API.
3
3
  * Import component classes to define zhanla components in TypeScript.
4
4
  */
5
- export { Tool, CodeEval, Skill, Agent, LLMProcessor, LLMEval, Runner, Conditional, Step, Orchestration, Leaf, Edge, Branch, Checklist, EvalTree, BaseComponent, RUNNABLE_TYPES, EVAL_TYPES, } from "./types.js";
5
+ export { Tool, CodeEval, Skill, Agent, LLMProcessor, LLMEval, Runner, Conditional, Step, Orchestration, Leaf, Edge, Branch, Checklist, EvalTree, ZhanlaComponent, RUNNABLE_TYPES, EVAL_TYPES, } from "./types.js";
6
6
  export { toManifest, collectExportedComponents } from "./manifest.js";
7
7
  export { executeComponent, executeRow } from "./executor.js";
8
8
  export { wrap } from "./wrap.js";
@@ -2,7 +2,7 @@
2
2
  * Manifest emission — serialize SDK component instances into ComponentManifest objects.
3
3
  * The manifest shape mirrors the Python CLI's ComponentManifest dataclass.
4
4
  */
5
- import { BaseComponent, ComponentRef, JsonSchema } from "./types.js";
5
+ import { ZhanlaComponent, ComponentRef, JsonSchema } from "./types.js";
6
6
  export interface StepManifest {
7
7
  name: string;
8
8
  component_ref?: ComponentRef;
@@ -62,8 +62,8 @@ export interface ComponentManifest {
62
62
  source_format?: string;
63
63
  model_response_format?: string;
64
64
  }
65
- export declare function toManifest(component: BaseComponent, opts?: {
65
+ export declare function toManifest(component: ZhanlaComponent, opts?: {
66
66
  filePath?: string;
67
67
  symbolName?: string;
68
68
  }): ComponentManifest;
69
- export declare function collectExportedComponents(moduleExports: Record<string, unknown>): BaseComponent[];
69
+ export declare function collectExportedComponents(moduleExports: Record<string, unknown>): ZhanlaComponent[];
package/dist/types.d.ts CHANGED
@@ -16,6 +16,7 @@ export type JsonSchemaArray = {
16
16
  items: JsonSchema;
17
17
  };
18
18
  export type JsonSchema = JsonSchemaScalar | JsonSchemaObject | JsonSchemaArray | Record<string, unknown>;
19
+ export type ComponentValue = string | number | Record<string, unknown> | unknown[];
19
20
  export type ComponentType = "tool" | "code_eval" | "skill" | "agent" | "llm_processor" | "llm_eval" | "orchestration" | "checklist" | "eval_tree";
20
21
  export interface ComponentRef {
21
22
  key: string;
@@ -60,7 +61,7 @@ export interface RunnerOptions {
60
61
  export declare class Runner {
61
62
  readonly client: unknown;
62
63
  constructor(opts: RunnerOptions);
63
- buildMessages(component: Pick<BaseComponent, "description"> & {
64
+ buildMessages(component: Pick<ZhanlaComponent, "description"> & {
64
65
  instructions?: string;
65
66
  }, row: Record<string, unknown>): RunnerMessage[];
66
67
  private providerName;
@@ -72,9 +73,10 @@ export declare class Runner {
72
73
  jsonRepair?: boolean;
73
74
  text: string;
74
75
  }): Promise<string>;
76
+ private warnModelProviderMismatch;
75
77
  callLlm(opts: RunnerCallOptions): Promise<LLMResponse>;
76
78
  }
77
- export declare abstract class BaseComponent {
79
+ export declare abstract class ZhanlaComponent {
78
80
  abstract readonly componentType: ComponentType;
79
81
  abstract readonly name: string;
80
82
  abstract readonly description: string;
@@ -90,7 +92,7 @@ export interface ToolOptions {
90
92
  outputSchema?: JsonSchema;
91
93
  key: string;
92
94
  }
93
- export declare class Tool extends BaseComponent {
95
+ export declare class Tool extends ZhanlaComponent {
94
96
  readonly componentType: "tool";
95
97
  readonly name: string;
96
98
  readonly description: string;
@@ -104,15 +106,15 @@ export declare class Tool extends BaseComponent {
104
106
  export interface CodeEvalOptions {
105
107
  name: string;
106
108
  description: string;
107
- fn: (...args: unknown[]) => unknown;
109
+ fn: (modelResponse: ComponentValue, expectedResponse: ComponentValue | null) => unknown;
108
110
  modelResponseFormat?: "JSON" | "TEXT" | "YAML";
109
111
  key: string;
110
112
  }
111
- export declare class CodeEval extends BaseComponent {
113
+ export declare class CodeEval extends ZhanlaComponent {
112
114
  readonly componentType: "code_eval";
113
115
  readonly name: string;
114
116
  readonly description: string;
115
- readonly fn: (...args: unknown[]) => unknown;
117
+ readonly fn: (modelResponse: ComponentValue, expectedResponse: ComponentValue | null) => unknown;
116
118
  readonly isAsync: boolean;
117
119
  readonly modelResponseFormat: "JSON" | "TEXT" | "YAML";
118
120
  readonly key: string;
@@ -127,7 +129,7 @@ export interface SkillOptions {
127
129
  outputSchema?: JsonSchema;
128
130
  key: string;
129
131
  }
130
- export declare class Skill extends BaseComponent {
132
+ export declare class Skill extends ZhanlaComponent {
131
133
  readonly componentType: "skill";
132
134
  readonly name: string;
133
135
  readonly description: string;
@@ -144,6 +146,7 @@ export interface AgentOptions {
144
146
  description: string;
145
147
  instructions: string;
146
148
  model: string;
149
+ client?: unknown;
147
150
  runner?: Runner;
148
151
  tools?: Tool[];
149
152
  skills?: Skill[];
@@ -154,7 +157,7 @@ export interface AgentOptions {
154
157
  topK?: number;
155
158
  key: string;
156
159
  }
157
- export declare class Agent extends BaseComponent {
160
+ export declare class Agent extends ZhanlaComponent {
158
161
  readonly componentType: "agent";
159
162
  readonly name: string;
160
163
  readonly description: string;
@@ -176,6 +179,7 @@ export interface LLMProcessorOptions {
176
179
  description: string;
177
180
  instructions: string;
178
181
  model: string;
182
+ client?: unknown;
179
183
  runner?: Runner;
180
184
  outputSchema?: JsonSchema;
181
185
  jsonRepair?: boolean;
@@ -183,7 +187,7 @@ export interface LLMProcessorOptions {
183
187
  topK?: number;
184
188
  key: string;
185
189
  }
186
- export declare class LLMProcessor extends BaseComponent {
190
+ export declare class LLMProcessor extends ZhanlaComponent {
187
191
  readonly componentType: "llm_processor";
188
192
  readonly name: string;
189
193
  readonly description: string;
@@ -202,6 +206,7 @@ export interface LLMEvalOptions {
202
206
  description: string;
203
207
  instructions: string;
204
208
  model: string;
209
+ client?: unknown;
205
210
  runner?: Runner;
206
211
  outputSchema?: JsonSchema;
207
212
  jsonRepair?: boolean;
@@ -209,7 +214,7 @@ export interface LLMEvalOptions {
209
214
  topK?: number;
210
215
  key: string;
211
216
  }
212
- export declare class LLMEval extends BaseComponent {
217
+ export declare class LLMEval extends ZhanlaComponent {
213
218
  readonly componentType: "llm_eval";
214
219
  readonly name: string;
215
220
  readonly description: string;
@@ -236,12 +241,12 @@ export declare class Conditional {
236
241
  }
237
242
  export interface StepOptions {
238
243
  name: string;
239
- component: BaseComponent | Conditional;
244
+ component: ZhanlaComponent | Conditional;
240
245
  next?: string[];
241
246
  }
242
247
  export declare class Step {
243
248
  readonly name: string;
244
- readonly component: BaseComponent | Conditional;
249
+ readonly component: ZhanlaComponent | Conditional;
245
250
  readonly next: string[];
246
251
  constructor(opts: StepOptions);
247
252
  }
@@ -251,7 +256,7 @@ export interface OrchestrationOptions {
251
256
  steps: Step[];
252
257
  key: string;
253
258
  }
254
- export declare class Orchestration extends BaseComponent {
259
+ export declare class Orchestration extends ZhanlaComponent {
255
260
  readonly componentType: "orchestration";
256
261
  readonly name: string;
257
262
  readonly description: string;
@@ -260,9 +265,9 @@ export declare class Orchestration extends BaseComponent {
260
265
  constructor(opts: OrchestrationOptions);
261
266
  }
262
267
  export declare class Leaf {
263
- readonly eval: BaseComponent;
268
+ readonly eval: ZhanlaComponent;
264
269
  constructor(opts: {
265
- eval: BaseComponent;
270
+ eval: ZhanlaComponent;
266
271
  });
267
272
  }
268
273
  export declare class Edge {
@@ -274,12 +279,12 @@ export declare class Edge {
274
279
  });
275
280
  }
276
281
  export declare class Branch {
277
- readonly eval: BaseComponent;
282
+ readonly eval: ZhanlaComponent;
278
283
  readonly threshold: number;
279
284
  readonly ifPass: Edge[];
280
285
  readonly ifFail: Edge[];
281
286
  constructor(opts: {
282
- eval: BaseComponent;
287
+ eval: ZhanlaComponent;
283
288
  threshold: number;
284
289
  ifPass: Edge[];
285
290
  ifFail: Edge[];
@@ -288,15 +293,15 @@ export declare class Branch {
288
293
  export interface ChecklistOptions {
289
294
  name: string;
290
295
  description: string;
291
- evals: BaseComponent[];
296
+ evals: ZhanlaComponent[];
292
297
  weights?: number[];
293
298
  key: string;
294
299
  }
295
- export declare class Checklist extends BaseComponent {
300
+ export declare class Checklist extends ZhanlaComponent {
296
301
  readonly componentType: "checklist";
297
302
  readonly name: string;
298
303
  readonly description: string;
299
- readonly evals: BaseComponent[];
304
+ readonly evals: ZhanlaComponent[];
300
305
  readonly weights?: number[];
301
306
  readonly key: string;
302
307
  constructor(opts: ChecklistOptions);
@@ -307,7 +312,7 @@ export interface EvalTreeOptions {
307
312
  root: Branch;
308
313
  key: string;
309
314
  }
310
- export declare class EvalTree extends BaseComponent {
315
+ export declare class EvalTree extends ZhanlaComponent {
311
316
  readonly componentType: "eval_tree";
312
317
  readonly name: string;
313
318
  readonly description: string;
package/dist/types.js CHANGED
@@ -263,7 +263,22 @@ export class Runner {
263
263
  }
264
264
  }
265
265
  }
266
+ warnModelProviderMismatch(model) {
267
+ const provider = this.providerName();
268
+ let expected;
269
+ if (model.startsWith("claude-"))
270
+ expected = "anthropic";
271
+ else if (model.startsWith("gemini-"))
272
+ expected = "gemini";
273
+ else if (/^(gpt-|o1-|o3-|o4-)/.test(model))
274
+ expected = "openai";
275
+ if (expected !== undefined && provider !== expected) {
276
+ console.warn(`[zhanla] Model '${model}' looks like an ${expected} model but the runner client is ${provider}. ` +
277
+ "Check that your client matches your model.");
278
+ }
279
+ }
266
280
  async callLlm(opts) {
281
+ this.warnModelProviderMismatch(opts.model);
267
282
  if (isAnthropicClient(this.client)) {
268
283
  const systemMessage = opts.messages.find((message) => message.role === "system")?.content ?? "";
269
284
  const chatMessages = opts.messages
@@ -426,7 +441,7 @@ export class Runner {
426
441
  // ---------------------------------------------------------------------------
427
442
  // Base component class
428
443
  // ---------------------------------------------------------------------------
429
- export class BaseComponent {
444
+ export class ZhanlaComponent {
430
445
  get isRunnable() {
431
446
  return RUNNABLE_TYPES.has(this.componentType);
432
447
  }
@@ -434,7 +449,7 @@ export class BaseComponent {
434
449
  return EVAL_TYPES.has(this.componentType);
435
450
  }
436
451
  }
437
- export class Tool extends BaseComponent {
452
+ export class Tool extends ZhanlaComponent {
438
453
  componentType = "tool";
439
454
  name;
440
455
  description;
@@ -455,7 +470,7 @@ export class Tool extends BaseComponent {
455
470
  this.key = opts.key;
456
471
  }
457
472
  }
458
- export class CodeEval extends BaseComponent {
473
+ export class CodeEval extends ZhanlaComponent {
459
474
  componentType = "code_eval";
460
475
  name;
461
476
  description;
@@ -474,7 +489,7 @@ export class CodeEval extends BaseComponent {
474
489
  this.key = opts.key;
475
490
  }
476
491
  }
477
- export class Skill extends BaseComponent {
492
+ export class Skill extends ZhanlaComponent {
478
493
  componentType = "skill";
479
494
  name;
480
495
  description;
@@ -497,7 +512,7 @@ export class Skill extends BaseComponent {
497
512
  this.key = opts.key;
498
513
  }
499
514
  }
500
- export class Agent extends BaseComponent {
515
+ export class Agent extends ZhanlaComponent {
501
516
  componentType = "agent";
502
517
  name;
503
518
  description;
@@ -514,11 +529,14 @@ export class Agent extends BaseComponent {
514
529
  key;
515
530
  constructor(opts) {
516
531
  super();
532
+ if (opts.client !== undefined && opts.runner !== undefined) {
533
+ throw new Error("Specify client or runner, not both.");
534
+ }
517
535
  this.name = opts.name;
518
536
  this.description = opts.description;
519
537
  this.instructions = opts.instructions;
520
538
  this.model = opts.model;
521
- this.runner = opts.runner;
539
+ this.runner = opts.client !== undefined ? new Runner({ client: opts.client }) : opts.runner;
522
540
  this.tools = opts.tools ?? [];
523
541
  this.skills = opts.skills ?? [];
524
542
  this.agents = opts.agents ?? [];
@@ -530,7 +548,7 @@ export class Agent extends BaseComponent {
530
548
  this.key = opts.key;
531
549
  }
532
550
  }
533
- export class LLMProcessor extends BaseComponent {
551
+ export class LLMProcessor extends ZhanlaComponent {
534
552
  componentType = "llm_processor";
535
553
  name;
536
554
  description;
@@ -544,11 +562,14 @@ export class LLMProcessor extends BaseComponent {
544
562
  key;
545
563
  constructor(opts) {
546
564
  super();
565
+ if (opts.client !== undefined && opts.runner !== undefined) {
566
+ throw new Error("Specify client or runner, not both.");
567
+ }
547
568
  this.name = opts.name;
548
569
  this.description = opts.description;
549
570
  this.instructions = opts.instructions;
550
571
  this.model = opts.model;
551
- this.runner = opts.runner;
572
+ this.runner = opts.client !== undefined ? new Runner({ client: opts.client }) : opts.runner;
552
573
  this.outputSchema = opts.outputSchema;
553
574
  this.jsonRepair = opts.jsonRepair ?? false;
554
575
  this.temperature = opts.temperature;
@@ -557,7 +578,7 @@ export class LLMProcessor extends BaseComponent {
557
578
  this.key = opts.key;
558
579
  }
559
580
  }
560
- export class LLMEval extends BaseComponent {
581
+ export class LLMEval extends ZhanlaComponent {
561
582
  componentType = "llm_eval";
562
583
  name;
563
584
  description;
@@ -571,11 +592,14 @@ export class LLMEval extends BaseComponent {
571
592
  key;
572
593
  constructor(opts) {
573
594
  super();
595
+ if (opts.client !== undefined && opts.runner !== undefined) {
596
+ throw new Error("Specify client or runner, not both.");
597
+ }
574
598
  this.name = opts.name;
575
599
  this.description = opts.description;
576
600
  this.instructions = opts.instructions;
577
601
  this.model = opts.model;
578
- this.runner = opts.runner;
602
+ this.runner = opts.client !== undefined ? new Runner({ client: opts.client }) : opts.runner;
579
603
  this.outputSchema = opts.outputSchema;
580
604
  this.jsonRepair = opts.jsonRepair ?? false;
581
605
  this.temperature = opts.temperature;
@@ -604,7 +628,7 @@ export class Step {
604
628
  this.next = opts.next ?? [];
605
629
  }
606
630
  }
607
- export class Orchestration extends BaseComponent {
631
+ export class Orchestration extends ZhanlaComponent {
608
632
  componentType = "orchestration";
609
633
  name;
610
634
  description;
@@ -648,7 +672,7 @@ export class Branch {
648
672
  this.ifFail = opts.ifFail;
649
673
  }
650
674
  }
651
- export class Checklist extends BaseComponent {
675
+ export class Checklist extends ZhanlaComponent {
652
676
  componentType = "checklist";
653
677
  name;
654
678
  description;
@@ -665,7 +689,7 @@ export class Checklist extends BaseComponent {
665
689
  this.key = opts.key;
666
690
  }
667
691
  }
668
- export class EvalTree extends BaseComponent {
692
+ export class EvalTree extends ZhanlaComponent {
669
693
  componentType = "eval_tree";
670
694
  name;
671
695
  description;
package/package.json CHANGED
@@ -1,12 +1,17 @@
1
1
  {
2
2
  "name": "@zhanla/sdk-ts",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "TypeScript SDK for the zhanla CLI — define and run AI components locally",
5
+ "homepage": "https://benchmark-black.vercel.app/",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/zhanla-ai"
9
+ },
5
10
  "main": "dist/index.js",
6
11
  "types": "dist/index.d.ts",
7
12
  "type": "module",
8
13
  "bin": {
9
- "zhanla-sdk-ts": "./bin/cli.js"
14
+ "zhanla-sdk-ts": "bin/cli.js"
10
15
  },
11
16
  "scripts": {
12
17
  "build": "tsc",