@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 +21 -0
- package/README.md +392 -0
- package/dist/executor/index.d.ts +7 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +21012 -0
- package/dist/parser/index.d.ts +8 -0
- package/dist/parser/template-string-parser.d.ts +2 -0
- package/dist/types.d.ts +64 -0
- package/dist/utils.d.ts +17 -0
- package/dist/validator/index.d.ts +2 -0
- package/package.json +56 -0
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;
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|