@tabbybyte/kimten 0.1.4 → 0.2.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/README.md +35 -33
- package/index.d.ts +32 -0
- package/index.js +5 -0
- package/lib/kimten.js +81 -2
- package/lib/memory.js +24 -0
- package/lib/tools.js +41 -9
- package/package.json +7 -2
- package/lib/prompt.js +0 -6
package/README.md
CHANGED
|
@@ -14,8 +14,8 @@ It’s meant to feel like a smart helper, not a framework.
|
|
|
14
14
|
|
|
15
15
|
## ✅ What it does
|
|
16
16
|
|
|
17
|
-
- Runs a simple agent loop (bounded by `hops`)
|
|
18
|
-
- Lets the model call your
|
|
17
|
+
- Runs a simple, single-agent loop (bounded by `hops`)
|
|
18
|
+
- Lets the LLM model (the brain) call your tool functions (the toys)
|
|
19
19
|
- Keeps short-term conversation memory (in-process, per instance)
|
|
20
20
|
- Supports optional structured output via Zod
|
|
21
21
|
|
|
@@ -24,19 +24,19 @@ It’s meant to feel like a smart helper, not a framework.
|
|
|
24
24
|
- No planners/graphs/state machines
|
|
25
25
|
- No streaming API surface
|
|
26
26
|
- No persistence or long-term memory
|
|
27
|
-
- No plugin system or orchestration
|
|
27
|
+
- No plugin system or multi-agent orchestration
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
31
31
|
## ✨ Why Kimten?
|
|
32
32
|
|
|
33
|
-
Use it when you just want an agent loop with
|
|
33
|
+
Use it when you just want an agent loop with toys and a little memory, without adopting a larger framework.
|
|
34
34
|
|
|
35
35
|
Good fits:
|
|
36
36
|
|
|
37
37
|
- CLI helpers
|
|
38
38
|
- small automations
|
|
39
|
-
- local
|
|
39
|
+
- local toys
|
|
40
40
|
- scripting
|
|
41
41
|
- quick AI utilities
|
|
42
42
|
- “just let the model call a function” use cases
|
|
@@ -68,9 +68,19 @@ const cat = Kimten({
|
|
|
68
68
|
brain: openai('gpt-4o-mini'), // or, any other available model
|
|
69
69
|
|
|
70
70
|
toys: {
|
|
71
|
-
|
|
71
|
+
randomNumber: {
|
|
72
|
+
description: 'Generate a random integer between min and max (inclusive).',
|
|
73
|
+
inputSchema: z.object({ min: z.number().int(), max: z.number().int() }),
|
|
74
|
+
async execute({ min, max }) {
|
|
75
|
+
const low = Math.min(min, max);
|
|
76
|
+
const high = Math.max(min, max);
|
|
77
|
+
return Math.floor(Math.random() * (high - low + 1)) + low;
|
|
78
|
+
},
|
|
79
|
+
},
|
|
72
80
|
},
|
|
73
81
|
|
|
82
|
+
personality: 'You are a helpful assistant.',
|
|
83
|
+
|
|
74
84
|
hops: 10,
|
|
75
85
|
});
|
|
76
86
|
|
|
@@ -119,19 +129,18 @@ Create a new instance.
|
|
|
119
129
|
|
|
120
130
|
#### Optional
|
|
121
131
|
|
|
122
|
-
* `toys` → object map of tool definitions. Each entry
|
|
123
|
-
* async function shorthand: `async (args) => result`
|
|
132
|
+
* `toys` → object map of toy (tool) definitions. Each entry is:
|
|
124
133
|
* object form: `{ inputSchema?, description?, strict?, execute }`
|
|
125
134
|
default: `{}`
|
|
126
|
-
* `personality` → system
|
|
127
|
-
* `hops` → max agent loop steps (default: `10`)
|
|
128
|
-
prevents infinite zoomies 🌀
|
|
135
|
+
* `personality` → system instructions / prompt for overall behavior description (default: `'You are a helpful assistant.'`)
|
|
136
|
+
* `hops` → max agent loop steps (default: `10`) - prevents infinite zoomies 🌀
|
|
129
137
|
|
|
130
|
-
####
|
|
138
|
+
#### Toy semantics
|
|
131
139
|
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
- If a
|
|
140
|
+
- Toy inputs are validated only if you provide `inputSchema`.
|
|
141
|
+
- Toy results should be JSON-serializable; `undefined` becomes `null`.
|
|
142
|
+
- If a toy function throws, Kimten returns `{ error, toolName }` as the toy result (it does not re-throw).
|
|
143
|
+
- Under the hood, each toy is implemented as an AI SDK tool.
|
|
135
144
|
|
|
136
145
|
#### Returns
|
|
137
146
|
|
|
@@ -153,32 +162,25 @@ Create a new instance.
|
|
|
153
162
|
|
|
154
163
|
For the `brain` part, feel free to use any compatible provider and their models.
|
|
155
164
|
|
|
156
|
-
|
|
165
|
+
❗ Note that not all providers (and models) may work out the box with Kimten, particularly for structured output.
|
|
157
166
|
|
|
158
|
-
|
|
167
|
+
💡 Refer to the AI SDK docs: **[providers and models](https://ai-sdk.dev/docs/foundations/providers-and-models)**.
|
|
159
168
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
```js
|
|
163
|
-
toys: {
|
|
164
|
-
readFile,
|
|
165
|
-
writeFile,
|
|
166
|
-
fetchJson,
|
|
167
|
-
runCommand,
|
|
168
|
-
}
|
|
169
|
-
```
|
|
169
|
+
### Add toys freely
|
|
170
170
|
|
|
171
|
-
|
|
171
|
+
Define `toys` in object form for strong arg validation and proper selection by the LLM:
|
|
172
172
|
|
|
173
173
|
```js
|
|
174
174
|
import { z } from 'zod';
|
|
175
175
|
|
|
176
176
|
toys: {
|
|
177
|
-
|
|
178
|
-
description: '
|
|
179
|
-
inputSchema: z.object({
|
|
180
|
-
async execute({
|
|
181
|
-
|
|
177
|
+
randomNumber: {
|
|
178
|
+
description: 'Generate a random integer between min and max (inclusive).',
|
|
179
|
+
inputSchema: z.object({ min: z.number().int(), max: z.number().int() }),
|
|
180
|
+
async execute({ min, max }) {
|
|
181
|
+
const low = Math.min(min, max);
|
|
182
|
+
const high = Math.max(min, max);
|
|
183
|
+
return Math.floor(Math.random() * (high - low + 1)) + low;
|
|
182
184
|
},
|
|
183
185
|
},
|
|
184
186
|
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ZodTypeAny, infer as ZodInfer } from 'zod';
|
|
2
|
+
|
|
3
|
+
export type BrainModel = Record<string, unknown>;
|
|
4
|
+
|
|
5
|
+
export type ToolExecute = (args: any) => any | Promise<any>;
|
|
6
|
+
|
|
7
|
+
export type ToyDefinition = {
|
|
8
|
+
inputSchema?: ZodTypeAny;
|
|
9
|
+
description?: string;
|
|
10
|
+
strict?: boolean;
|
|
11
|
+
execute: ToolExecute;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type Toys = Record<string, ToyDefinition>;
|
|
15
|
+
|
|
16
|
+
export type KimtenConfig = {
|
|
17
|
+
brain: BrainModel;
|
|
18
|
+
toys?: Toys;
|
|
19
|
+
personality?: string;
|
|
20
|
+
hops?: number;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type KimtenAgent = {
|
|
24
|
+
play(input: string): Promise<string>;
|
|
25
|
+
play<S extends ZodTypeAny>(input: string, schema: S): Promise<ZodInfer<S>>;
|
|
26
|
+
forget(): void;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export declare function Kimten(config: KimtenConfig): KimtenAgent;
|
|
30
|
+
|
|
31
|
+
declare const _default: typeof Kimten;
|
|
32
|
+
export default _default;
|
package/index.js
CHANGED
package/lib/kimten.js
CHANGED
|
@@ -1,10 +1,68 @@
|
|
|
1
1
|
import { ToolLoopAgent, stepCountIs, Output } from 'ai';
|
|
2
2
|
import { createMemory } from './memory.js';
|
|
3
|
-
import { buildMessages } from './prompt.js';
|
|
4
3
|
import { normalizeToys } from './tools.js';
|
|
5
4
|
|
|
6
5
|
const DEFAULT_PERSONALITY = 'You are a helpful assistant.';
|
|
7
6
|
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import('zod').ZodTypeAny} ZodSchema
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A minimal AI SDK model-like object accepted by Kimten.
|
|
13
|
+
*
|
|
14
|
+
* This is intentionally loose to avoid coupling Kimten to AI SDK internal types,
|
|
15
|
+
* while still providing useful IDE hints.
|
|
16
|
+
*
|
|
17
|
+
* @typedef {object} BrainModel
|
|
18
|
+
* @property {string} [specificationVersion]
|
|
19
|
+
* @property {string} [provider]
|
|
20
|
+
* @property {string} [modelId]
|
|
21
|
+
* @property {Record<string, unknown>} [supportedUrls]
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Tool execute function.
|
|
26
|
+
*
|
|
27
|
+
* @callback ToolExecute
|
|
28
|
+
* @param {any} args
|
|
29
|
+
* @returns {any | Promise<any>}
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Object-form tool definition.
|
|
34
|
+
*
|
|
35
|
+
* @typedef {object} ToyDefinition
|
|
36
|
+
* @property {import('zod').ZodTypeAny} [inputSchema]
|
|
37
|
+
* @property {string} [description]
|
|
38
|
+
* @property {boolean} [strict]
|
|
39
|
+
* @property {ToolExecute} execute
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Tool registry map.
|
|
44
|
+
*
|
|
45
|
+
* @typedef {Record<string, ToyDefinition>} Toys
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Kimten factory config.
|
|
50
|
+
*
|
|
51
|
+
* @typedef {object} KimtenConfig
|
|
52
|
+
* @property {BrainModel} brain AI SDK model instance.
|
|
53
|
+
* @property {Toys} [toys] Tool registry.
|
|
54
|
+
* @property {string} [personality] System prompt / instructions.
|
|
55
|
+
* @property {number} [hops] Max loop steps (prevents infinite loops).
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Returned Kimten instance.
|
|
60
|
+
*
|
|
61
|
+
* @typedef {object} KimtenAgent
|
|
62
|
+
* @property {(input: string, schema?: ZodSchema | null) => Promise<any>} play Run the agent loop.
|
|
63
|
+
* @property {() => void} forget Clear short-term memory.
|
|
64
|
+
*/
|
|
65
|
+
|
|
8
66
|
function validateConfig(config) {
|
|
9
67
|
if (config === null || typeof config !== 'object' || Array.isArray(config)) {
|
|
10
68
|
throw new TypeError('Kimten requires a config object.');
|
|
@@ -33,6 +91,12 @@ function validateConfig(config) {
|
|
|
33
91
|
};
|
|
34
92
|
}
|
|
35
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Create a tiny tool-using agent with short-term memory.
|
|
96
|
+
*
|
|
97
|
+
* @param {KimtenConfig} config
|
|
98
|
+
* @returns {KimtenAgent}
|
|
99
|
+
*/
|
|
36
100
|
export function Kimten(config) {
|
|
37
101
|
const { brain, toys, personality, hops } = validateConfig(config);
|
|
38
102
|
const memory = createMemory();
|
|
@@ -71,6 +135,16 @@ export function Kimten(config) {
|
|
|
71
135
|
return createStructuredAgent(schema);
|
|
72
136
|
}
|
|
73
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Run the agent loop.
|
|
140
|
+
*
|
|
141
|
+
* - Stores the conversation in short-term memory (in-process, per instance).
|
|
142
|
+
* - If `schema` is provided, returns structured output (via AI SDK output mode).
|
|
143
|
+
*
|
|
144
|
+
* @param {string} input
|
|
145
|
+
* @param {ZodSchema | null} [schema]
|
|
146
|
+
* @returns {Promise<any>}
|
|
147
|
+
*/
|
|
74
148
|
async function play(input, schema = null) {
|
|
75
149
|
if (typeof input !== 'string') {
|
|
76
150
|
throw new TypeError('Kimten play(input) expects input to be a string.');
|
|
@@ -80,7 +154,7 @@ export function Kimten(config) {
|
|
|
80
154
|
|
|
81
155
|
const agent = schema ? getStructuredAgent(schema) : textAgent;
|
|
82
156
|
const result = await agent.generate({
|
|
83
|
-
messages:
|
|
157
|
+
messages: memory.list(),
|
|
84
158
|
});
|
|
85
159
|
|
|
86
160
|
const assistantContent =
|
|
@@ -95,6 +169,11 @@ export function Kimten(config) {
|
|
|
95
169
|
return schema ? result.output : assistantContent;
|
|
96
170
|
}
|
|
97
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Clear short-term memory for this instance.
|
|
174
|
+
*
|
|
175
|
+
* @returns {void}
|
|
176
|
+
*/
|
|
98
177
|
function forget() {
|
|
99
178
|
memory.clear();
|
|
100
179
|
}
|
package/lib/memory.js
CHANGED
|
@@ -1,8 +1,32 @@
|
|
|
1
1
|
export const MEMORY_LIMIT = 10;
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* A single chat message stored in short-term memory.
|
|
5
|
+
*
|
|
6
|
+
* @typedef {object} MemoryMessage
|
|
7
|
+
* @property {'system' | 'user' | 'assistant' | 'tool'} role
|
|
8
|
+
* @property {any} content
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Short-term FIFO memory store (sliding window).
|
|
13
|
+
*
|
|
14
|
+
* @typedef {object} MemoryStore
|
|
15
|
+
* @property {(message: MemoryMessage) => void} add
|
|
16
|
+
* @property {() => MemoryMessage[]} list
|
|
17
|
+
* @property {() => void} clear
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Create a short-term FIFO memory store.
|
|
22
|
+
*
|
|
23
|
+
* @param {number} [limit=MEMORY_LIMIT] Max number of messages to keep.
|
|
24
|
+
* @returns {MemoryStore}
|
|
25
|
+
*/
|
|
3
26
|
export function createMemory(limit = MEMORY_LIMIT) {
|
|
4
27
|
const history = [];
|
|
5
28
|
|
|
29
|
+
/** @param {MemoryMessage} message */
|
|
6
30
|
function add(message) {
|
|
7
31
|
history.push(message);
|
|
8
32
|
if (history.length > limit) {
|
package/lib/tools.js
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
import { tool } from 'ai';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import('zod').ZodTypeAny} ZodSchema
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Tool execute function.
|
|
10
|
+
*
|
|
11
|
+
* @callback ToolExecute
|
|
12
|
+
* @param {any} args
|
|
13
|
+
* @returns {any | Promise<any>}
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Object-form tool definition.
|
|
18
|
+
*
|
|
19
|
+
* @typedef {object} ToyDefinition
|
|
20
|
+
* @property {ZodSchema} [inputSchema]
|
|
21
|
+
* @property {string} [description]
|
|
22
|
+
* @property {boolean} [strict]
|
|
23
|
+
* @property {ToolExecute} execute
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Tool registry map.
|
|
28
|
+
*
|
|
29
|
+
* @typedef {Record<string, ToyDefinition>} Toys
|
|
30
|
+
*/
|
|
31
|
+
|
|
4
32
|
function isPlainObject(value) {
|
|
5
33
|
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
6
34
|
return false;
|
|
@@ -10,13 +38,24 @@ function isPlainObject(value) {
|
|
|
10
38
|
return proto === Object.prototype || proto === null;
|
|
11
39
|
}
|
|
12
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Normalize a toy registry into AI SDK tool objects.
|
|
43
|
+
*
|
|
44
|
+
* - Object form only: `{ inputSchema?, description?, strict?, execute }`
|
|
45
|
+
*
|
|
46
|
+
* Tool execution is wrapped so thrown errors become JSON-safe results:
|
|
47
|
+
* `{ error, toolName }` (Kimten does not re-throw tool errors).
|
|
48
|
+
*
|
|
49
|
+
* @param {Toys | null | undefined} toys
|
|
50
|
+
* @returns {Record<string, ReturnType<typeof tool>>}
|
|
51
|
+
*/
|
|
13
52
|
export function normalizeToys(toys) {
|
|
14
53
|
if (toys === undefined || toys === null) {
|
|
15
54
|
return {};
|
|
16
55
|
}
|
|
17
56
|
|
|
18
57
|
if (!isPlainObject(toys)) {
|
|
19
|
-
throw new TypeError('Kimten config "toys" must be an object map of
|
|
58
|
+
throw new TypeError('Kimten config "toys" must be an object map of tool definitions.');
|
|
20
59
|
}
|
|
21
60
|
|
|
22
61
|
const wrapped = {};
|
|
@@ -46,16 +85,9 @@ export function normalizeToys(toys) {
|
|
|
46
85
|
}
|
|
47
86
|
|
|
48
87
|
function normalizeToyDefinition(name, entry) {
|
|
49
|
-
if (typeof entry === 'function') {
|
|
50
|
-
return {
|
|
51
|
-
inputSchema: z.any(),
|
|
52
|
-
execute: entry,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
88
|
if (!isPlainObject(entry)) {
|
|
57
89
|
throw new TypeError(
|
|
58
|
-
`Kimten tool "${name}" must be
|
|
90
|
+
`Kimten tool "${name}" must be an object with execute(args).`
|
|
59
91
|
);
|
|
60
92
|
}
|
|
61
93
|
|
package/package.json
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tabbybyte/kimten",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
7
|
"description": "A micro-agent library: thin wrapper over the Agent interface from Vercel's AI SDK Core v6+",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"main": "index.js",
|
|
10
|
+
"types": "index.d.ts",
|
|
10
11
|
"exports": {
|
|
11
|
-
".":
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./index.d.ts",
|
|
14
|
+
"default": "./index.js"
|
|
15
|
+
}
|
|
12
16
|
},
|
|
13
17
|
"files": [
|
|
14
18
|
"index.js",
|
|
19
|
+
"index.d.ts",
|
|
15
20
|
"lib"
|
|
16
21
|
],
|
|
17
22
|
"scripts": {
|