@kirha/planner 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kirha
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,392 @@
1
+ # @kirha/planner
2
+
3
+ SDK to interact with, parse, validate, and execute execution plans from [kirha/planner](https://huggingface.co/kirha/planner) - a fine-tuned LLM for tool planning. See the [kirha/kirha-planner collection](https://huggingface.co/collections/kirha/kirha-planner) for quantized models.
4
+
5
+ Compatible with any OpenAI chat completion compatible endpoint.
6
+
7
+ ## Overview
8
+
9
+ This SDK is designed to work with [kirha/planner](https://huggingface.co/kirha/planner), a Qwen3 8b model fine-tuned to generate complete DAG (Directed Acyclic Graph) execution plans from natural language queries.
10
+
11
+ Instead of step-by-step function calling, the model outputs a full execution plan in one pass. This SDK handles:
12
+
13
+ - **Interaction**: Send queries to the model with your tool definitions
14
+ - **Parsing**: Extract and validate the structured plan from model output
15
+ - **Validation**: Verify plan correctness against tool schemas (dependency references, type mismatches)
16
+ - **Execution**: Run all steps with automatic dependency resolution
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @kirha/planner
22
+ # or
23
+ bun add @kirha/planner
24
+ ```
25
+
26
+ ## Running the Model
27
+
28
+ On Mac (Apple Silicon), you can run the model locally using MLX:
29
+
30
+ ```bash
31
+ # Install mlx-lm
32
+ pip install mlx-lm
33
+
34
+ # Start the server
35
+ mlx_lm.server --model kirha/planner-mlx-4bit
36
+ ```
37
+
38
+ The server will start on `http://localhost:8080` with an OpenAI-compatible API.
39
+
40
+ Any OpenAI chat completion compatible endpoint can be used (vLLM, Ollama, etc.).
41
+
42
+ ## Quick Start
43
+
44
+ ```typescript
45
+ import { Planner } from "@kirha/planner";
46
+
47
+ // Define your tools
48
+ const tools = [
49
+ {
50
+ name: "get_weather",
51
+ description: "Get current weather for a city",
52
+ inputSchema: JSON.stringify({
53
+ type: "object",
54
+ properties: {
55
+ city: { type: "string", description: "City name" },
56
+ },
57
+ required: ["city"],
58
+ }),
59
+ outputSchema: JSON.stringify({
60
+ type: "object",
61
+ properties: {
62
+ temperature: { type: "number" },
63
+ condition: { type: "string" },
64
+ },
65
+ }),
66
+ handler: async ({ city }) => ({
67
+ temperature: 22,
68
+ condition: "sunny",
69
+ }),
70
+ },
71
+ ];
72
+
73
+ // Create a planner instance
74
+ const planner = new Planner("http://localhost:8080/v1", {
75
+ model: "kirha/planner",
76
+ });
77
+
78
+ // Generate a plan from natural language
79
+ const plan = await planner.generatePlan("What's the weather in Paris?", {
80
+ tools,
81
+ });
82
+
83
+ // Execute the plan
84
+ if (plan) {
85
+ const results = await plan.execute({ tools });
86
+ console.log(results);
87
+ }
88
+ ```
89
+
90
+ ## API Reference
91
+
92
+ ### `Planner`
93
+
94
+ Main class for generating execution plans.
95
+
96
+ ```typescript
97
+ const planner = new Planner(baseUrl: string, options: {
98
+ apiKey?: string; // API key for authentication
99
+ model?: string; // Model name (default: "kirha/planner")
100
+ });
101
+ ```
102
+
103
+ #### `planner.generatePlan(query, options)`
104
+
105
+ Generates an execution plan from a natural language query.
106
+
107
+ ```typescript
108
+ const plan = await planner.generatePlan(query: string, {
109
+ tools: Tool[]; // Available tools
110
+ instructions?: string; // Additional instructions for the model
111
+ temperature?: number; // Sampling temperature (default: 0)
112
+ maxTokens?: number; // Max tokens (default: 10000)
113
+ });
114
+ ```
115
+
116
+ ### `Plan`
117
+
118
+ Represents a generated execution plan.
119
+
120
+ #### `plan.execute(options)`
121
+
122
+ Executes all steps in the plan with automatic dependency resolution.
123
+
124
+ ```typescript
125
+ const results = await plan.execute({
126
+ tools: Tool[]; // Tool implementations with handlers
127
+ });
128
+ ```
129
+
130
+ ### Types
131
+
132
+ #### `Tool`
133
+
134
+ ```typescript
135
+ interface Tool<TInput = unknown, TOutput = unknown> {
136
+ name: string;
137
+ description: string;
138
+ inputSchema: string; // JSON Schema as string
139
+ outputSchema: string; // JSON Schema as string
140
+ handler: (args: TInput) => Promise<TOutput>;
141
+ }
142
+ ```
143
+
144
+ #### `StepResult`
145
+
146
+ ```typescript
147
+ interface StepResult {
148
+ stepId: string;
149
+ toolName: string;
150
+ arguments: Record<string, unknown>;
151
+ output: unknown;
152
+ error?: string;
153
+ }
154
+ ```
155
+
156
+ ## Parsing
157
+
158
+ The SDK parses the model's raw output into a structured, executable plan.
159
+
160
+ ### Model Output Format
161
+
162
+ The model generates a `<think>` block for reasoning followed by a `<plan>` block:
163
+
164
+ ```
165
+ <think>
166
+ I need to first get the user's location, then fetch the weather.
167
+ </think>
168
+ <plan>
169
+ [
170
+ {
171
+ "thought": "Get user location",
172
+ "toolName": "get_location",
173
+ "arguments": { "userId": "123" }
174
+ },
175
+ {
176
+ "thought": "Get weather for the location",
177
+ "toolName": "get_weather",
178
+ "arguments": { "city": "{0.city}" }
179
+ }
180
+ ]
181
+ </plan>
182
+ ```
183
+
184
+ ### Step Structure
185
+
186
+ Each step in the plan contains:
187
+
188
+ ```typescript
189
+ {
190
+ stepId: string; // Unique identifier for this step
191
+ toolName: string; // Name of the tool to execute
192
+ arguments: object; // Parameters with possible references
193
+ thought?: string; // Model's reasoning for this step
194
+ }
195
+ ```
196
+
197
+ ### Dependency References
198
+
199
+ Steps can reference outputs from previous steps. The parser transforms these into structured references.
200
+
201
+ #### Template String References
202
+
203
+ When a string contains `{index}` or `{index.path}` patterns, it's parsed as a template:
204
+
205
+ ```json
206
+ // Model output
207
+ { "city": "{0.location}" }
208
+
209
+ // Parsed to
210
+ {
211
+ "city": {
212
+ "$fromTemplateString": "{0}",
213
+ "$values": [
214
+ { "$fromStep": "0", "$outputKey": "location" }
215
+ ]
216
+ }
217
+ }
218
+ ```
219
+
220
+ With multiple references or surrounding text:
221
+
222
+ ```json
223
+ // Model output
224
+ { "message": "Weather in {0.city}: {1.temperature}°C" }
225
+
226
+ // Parsed to
227
+ {
228
+ "message": {
229
+ "$fromTemplateString": "Weather in {0}: {1}°C",
230
+ "$values": [
231
+ { "$fromStep": "0", "$outputKey": "city" },
232
+ { "$fromStep": "1", "$outputKey": "temperature" }
233
+ ]
234
+ }
235
+ }
236
+ ```
237
+
238
+ #### Object References
239
+
240
+ The model can also output explicit reference objects:
241
+
242
+ ```json
243
+ // Model output
244
+ { "city": { "fromStep": 0, "outputKey": "location" } }
245
+
246
+ // Parsed to
247
+ { "city": { "$fromStep": "0", "$outputKey": "location" } }
248
+ ```
249
+
250
+ #### Supported Patterns
251
+
252
+ Template string patterns:
253
+
254
+ - `{0}` - Reference entire output from step 0
255
+ - `{0.field}` - Reference `field` from step 0's output
256
+ - `{1.data.nested}` - Reference nested field from step 1
257
+ - `{0.items[0].name}` - Reference array element (bracket notation converted to dot notation)
258
+
259
+ ## Validation
260
+
261
+ The SDK can validate a parsed plan against your tool definitions before execution, catching issues like missing tools, invalid dependency references, and type mismatches.
262
+
263
+ ### `isValidPlan(steps, tools)`
264
+
265
+ ```typescript
266
+ import { isValidPlan } from "@kirha/planner";
267
+
268
+ const result = isValidPlan(plan.steps, tools);
269
+
270
+ if (!result.valid) {
271
+ console.error("Plan validation errors:", result.errors);
272
+ }
273
+ ```
274
+
275
+ ### `PlanValidationResult`
276
+
277
+ ```typescript
278
+ interface PlanValidationResult {
279
+ valid: boolean;
280
+ errors: PlanValidationError[];
281
+ }
282
+ ```
283
+
284
+ ### `PlanValidationError`
285
+
286
+ ```typescript
287
+ interface PlanValidationError {
288
+ code: string;
289
+ message: string;
290
+ stepId?: string;
291
+ toolName?: string;
292
+ argumentPath?: string;
293
+ fromStepId?: string;
294
+ outputPath?: string;
295
+ expectedType?: string;
296
+ actualType?: string;
297
+ }
298
+ ```
299
+
300
+ ## Execution
301
+
302
+ The executor runs all steps with automatic dependency resolution and parallel execution where possible.
303
+
304
+ ### Execution Flow
305
+
306
+ 1. **Dependency Analysis**: Extract all step dependencies from arguments
307
+ 2. **Eager Execution**: Steps without pending dependencies start immediately
308
+ 3. **Parallel Execution**: Independent steps run concurrently
309
+ 4. **Resolution**: When a step completes, its dependents become eligible
310
+ 5. **Ordering**: Results are returned in original step order
311
+
312
+ ### DAG Execution
313
+
314
+ The executor treats the plan as a Directed Acyclic Graph (DAG).
315
+
316
+ **Query**: _"What is the profit and loss of the largest USDC holder on Base?"_
317
+
318
+ ```json
319
+ [
320
+ { "toolName": "getChainId", "arguments": { "blockchain": "Base" } },
321
+ { "toolName": "searchCoin", "arguments": { "query": "USDC", "limit": 1 } },
322
+ {
323
+ "toolName": "getCoinPlatformInfo",
324
+ "arguments": {
325
+ "coinId": { "fromStep": 1, "outputKey": "coins.0.id" },
326
+ "platform": "base"
327
+ }
328
+ },
329
+ {
330
+ "toolName": "getTokenHolders",
331
+ "arguments": {
332
+ "chainId": { "fromStep": 0, "outputKey": "chainId" },
333
+ "tokenAddress": { "fromStep": 2, "outputKey": "contractAddress" },
334
+ "limit": 1
335
+ }
336
+ },
337
+ {
338
+ "toolName": "getWalletPnL",
339
+ "arguments": {
340
+ "address": { "fromStep": 3, "outputKey": "holders.0.address" }
341
+ }
342
+ }
343
+ ]
344
+ ```
345
+
346
+ Execution graph:
347
+
348
+ ```
349
+ Step 0: getChainId Step 1: searchCoin
350
+ │ │
351
+ │ ↓
352
+ │ Step 2: getCoinPlatformInfo
353
+ │ │
354
+ └────────────┬────────────┘
355
+
356
+ Step 3: getTokenHolders
357
+
358
+
359
+ Step 4: getWalletPnL
360
+ ```
361
+
362
+ Execution timeline:
363
+
364
+ 1. Steps 0 and 1 start in parallel (no dependencies)
365
+ 2. Step 2 starts once step 1 completes
366
+ 3. Step 3 waits for both steps 0 and 2 to complete
367
+ 4. Step 4 starts once step 3 completes
368
+
369
+ ### Error Handling
370
+
371
+ - **Tool not found**: Step is skipped with an error message
372
+ - **Argument resolution failed**: Step is skipped
373
+ - **Tool execution failed**: Step marked as failed, dependents are skipped
374
+ - **Unsatisfied dependencies**: Steps are skipped at the end
375
+
376
+ ### Step Results
377
+
378
+ Each step returns a `StepResult`:
379
+
380
+ ```typescript
381
+ {
382
+ stepId: string; // Step identifier
383
+ toolName: string; // Tool that was called
384
+ arguments: Record<string, unknown>; // Resolved arguments
385
+ output: unknown; // Tool return value
386
+ error?: string; // Error message if failed
387
+ }
388
+ ```
389
+
390
+ ## License
391
+
392
+ MIT
@@ -0,0 +1,7 @@
1
+ import { type PlanStep, type Tool, type StepResult } from "../types";
2
+ export interface ExecuteOptions {
3
+ tools: Tool[];
4
+ }
5
+ export declare function executePlan(steps: PlanStep[], options: ExecuteOptions): Promise<StepResult[]>;
6
+ export declare function resolveArguments(args: Record<string, unknown>, outputsByStepId: Map<string, unknown>): Record<string, unknown>;
7
+ export declare function resolveValue(value: unknown, outputsByStepId: Map<string, unknown>): unknown;
@@ -0,0 +1,30 @@
1
+ import OpenAI from "openai";
2
+ import { parsePlanSteps } from "./parser";
3
+ import { type ExecuteOptions } from "./executor";
4
+ import type { PlanStep, Tool, StepResult } from "./types";
5
+ export type { Tool, ToolHandler, StepResult, PlanValidationError, PlanValidationResult, } from "./types";
6
+ export type { ExecuteOptions } from "./executor";
7
+ export { isValidPlan } from "./validator";
8
+ export { parsePlanSteps };
9
+ export declare const LATEST_MODEL_NAME = "kirha/planner";
10
+ export interface PlanOptions {
11
+ tools: Tool[];
12
+ instructions?: string;
13
+ temperature?: number;
14
+ maxTokens?: number;
15
+ }
16
+ export declare class Plan {
17
+ private steps;
18
+ private think?;
19
+ constructor(steps: PlanStep[], think?: string | undefined);
20
+ execute(options: ExecuteOptions): Promise<StepResult[]>;
21
+ }
22
+ export declare class Planner {
23
+ openai: OpenAI;
24
+ private model;
25
+ constructor(baseUrl: string, { apiKey, model, }: {
26
+ apiKey?: string;
27
+ model?: string;
28
+ });
29
+ generatePlan(query: string, options: PlanOptions): Promise<Plan | undefined>;
30
+ }