@atgs/tapeworm 0.0.1 → 0.1.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/README.md +120 -0
- package/dist/agent/agent.d.ts +61 -0
- package/dist/conversation/conversation.d.ts +47 -0
- package/dist/conversation/message.d.ts +189 -0
- package/dist/index.d.ts +8 -0
- package/dist/model/OllamaModel.d.ts +48 -0
- package/dist/model/model.d.ts +56 -0
- package/dist/tapeworm.cjs.js +1 -1
- package/dist/tapeworm.es.js +597 -218
- package/dist/tapeworm.umd.js +1 -1
- package/dist/tool/tool.d.ts +35 -0
- package/dist/tool/toolCall.d.ts +84 -0
- package/dist/tool/toolschema.d.ts +106 -0
- package/package.json +8 -4
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# Tapeworm
|
|
4
|
+
|
|
5
|
+
Tapeworm is an in-browser and node agent framework.
|
|
6
|
+
|
|
7
|
+
This is the root package for Tapeworm. You can consume other packages like @atgs/tapeworm_bedrock for AWS Bedrock support.
|
|
8
|
+
|
|
9
|
+
It provides an object-oriented API to create agents that run either on Node or within the browser.
|
|
10
|
+
|
|
11
|
+
**This project is currently in alpha and is under active development.**
|
|
12
|
+
|
|
13
|
+
## Current Features
|
|
14
|
+
|
|
15
|
+
- Base API Defined
|
|
16
|
+
- Supports `function` tools
|
|
17
|
+
- Supports Ollama models
|
|
18
|
+
|
|
19
|
+
## Example
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
import {Agent, OllamaModel, Parameter, Tool, ToolSchema} from '../../dist/tapeworm.es.js';
|
|
23
|
+
|
|
24
|
+
class SomeTool extends Tool {
|
|
25
|
+
|
|
26
|
+
// Get the name of the function
|
|
27
|
+
getName() {
|
|
28
|
+
return "AdditionTool";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Tell models what this function does
|
|
32
|
+
getDescription() {
|
|
33
|
+
return "Adds two numbers together.";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Define the tool schema
|
|
37
|
+
getToolSchema() {
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Actually run the code!
|
|
42
|
+
execute(input) {
|
|
43
|
+
let a = input.a;
|
|
44
|
+
let b = input.b;
|
|
45
|
+
return a + b;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const ollama = new Model();
|
|
51
|
+
|
|
52
|
+
const agent = new Agent();
|
|
53
|
+
agent.name = "agent";
|
|
54
|
+
agent.tools = [new YourTool(), new YourOtherTool()]
|
|
55
|
+
agent.system_prompt = "You are an agent "
|
|
56
|
+
agent.model = ollama;
|
|
57
|
+
|
|
58
|
+
await agent.invoke("What is 9 + 10"); // Most likely doesn't print 21.
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Roadmap
|
|
63
|
+
|
|
64
|
+
Tapeworm seeks to be the most ergonomic agentic solution for Node and the browser.
|
|
65
|
+
|
|
66
|
+
It has a long way to go before this project is there, but I believe we're off to a good start.
|
|
67
|
+
|
|
68
|
+
Here are the main areas that will be addressed:
|
|
69
|
+
|
|
70
|
+
### Better Agent Definitions
|
|
71
|
+
|
|
72
|
+
Agents will have a dedicated builder, and I will attempt to make tool definitions a lot easier to write so that the `getName()`, `getDescription()`, and `getToolSchema()` can be generated from your JSDoc or with annotations.
|
|
73
|
+
|
|
74
|
+
In the meantime, schemas are easy to define with builders. See the example calculator bot for an example.
|
|
75
|
+
|
|
76
|
+
### More documentation and examples
|
|
77
|
+
|
|
78
|
+
We will build JS Docs and expose them, and more examples.
|
|
79
|
+
|
|
80
|
+
### Plug 'n Play Proxy Servers
|
|
81
|
+
|
|
82
|
+
AI Proxy servers are lightweight wrappers around your inference provider. If you're writing a Node app, you'll likely not need one, but if you're writing a browser application and want tools executing in the browser, they're likely necessary for security unless you're writing a prototype, because AI proxy servers allow you to keep your API keys private and meter consumption.
|
|
83
|
+
|
|
84
|
+
A production-ready proxy server targeting Node will be available from this repository. It will offer these features:
|
|
85
|
+
|
|
86
|
+
- Very simple configuration
|
|
87
|
+
- Portability/modularity in your existing node applications
|
|
88
|
+
- Ability to integrate into Redis or other databases for per-IP/per-customer metering
|
|
89
|
+
|
|
90
|
+
### Providers
|
|
91
|
+
|
|
92
|
+
Tapeworm has only a built-in implementation for Ollama models at the moment.
|
|
93
|
+
|
|
94
|
+
Soon, this will change. I will be targeting newer providers in this order:
|
|
95
|
+
|
|
96
|
+
- Your own proxy server
|
|
97
|
+
- AWS Bedrock
|
|
98
|
+
- Vertex AI
|
|
99
|
+
- OpenAI API
|
|
100
|
+
- HuggingFace API
|
|
101
|
+
|
|
102
|
+
### Streaming support with obfuscation
|
|
103
|
+
|
|
104
|
+
Tapeworm is synchronous at the moment. The Agent and Model interfaces will be expanded to support streaming. Further, they will be implemented in such a way to prevent side channel attacks.
|
|
105
|
+
|
|
106
|
+
### Conversation Managers
|
|
107
|
+
|
|
108
|
+
Summarizing and RAG-based conversation managers will be implemented to prevent token overflow errors.
|
|
109
|
+
|
|
110
|
+
### Orchestration
|
|
111
|
+
|
|
112
|
+
Swarms, teams, and graphs will become first-class features of Tapeworm, along with the tools they require to run efficiently.
|
|
113
|
+
|
|
114
|
+
### Built-in tools
|
|
115
|
+
|
|
116
|
+
More built-in tools will be added. Would love to get a code interpreter for JS working.
|
|
117
|
+
|
|
118
|
+
## Any suggestions?
|
|
119
|
+
|
|
120
|
+
Feel free to leave an issue.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import Conversation, { ConversationManager } from "../conversation/conversation";
|
|
2
|
+
import Message from "../conversation/message";
|
|
3
|
+
import { type Model } from "../model/model";
|
|
4
|
+
import type Tool from "../tool/tool";
|
|
5
|
+
import type ToolCall from "../tool/toolCall";
|
|
6
|
+
/**
|
|
7
|
+
* Coordinates a model, its tools, and the running conversation to fulfill user queries.
|
|
8
|
+
*/
|
|
9
|
+
export default class Agent {
|
|
10
|
+
name: string;
|
|
11
|
+
systemPrompt?: string;
|
|
12
|
+
tools: Tool[];
|
|
13
|
+
model: Model;
|
|
14
|
+
conversation: Conversation;
|
|
15
|
+
conversationManager?: ConversationManager;
|
|
16
|
+
toolNameToIndexMap: any | undefined;
|
|
17
|
+
callback: (m: Message) => void;
|
|
18
|
+
constructor(name: string, tools: Tool[], model: Model, conversationManager: ConversationManager, callback: (m: Message) => void);
|
|
19
|
+
/**
|
|
20
|
+
* Run the full agent loop for a user query: seed the conversation, invoke the model,
|
|
21
|
+
* and execute any returned tool calls until completion.
|
|
22
|
+
* @param query User-provided input to hand to the agent.
|
|
23
|
+
*/
|
|
24
|
+
invoke(query: string): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Ask the backing model for the next response given the current conversation state.
|
|
27
|
+
* @returns Parsed model response including content, thinking, and tool calls.
|
|
28
|
+
*/
|
|
29
|
+
_runQuery(): Promise<Message>;
|
|
30
|
+
/**
|
|
31
|
+
* Execute a single tool call and append the result (or error) back to the conversation.
|
|
32
|
+
* @param toolCall Tool invocation details returned by the model.
|
|
33
|
+
* @returns Tool execution output, if any.
|
|
34
|
+
*/
|
|
35
|
+
_runTool(toolCall: ToolCall): Promise<any>;
|
|
36
|
+
/**
|
|
37
|
+
* Build a lookup from tool name to index for efficient resolution of tool calls.
|
|
38
|
+
*/
|
|
39
|
+
generateToolNameToIndexMap(): void;
|
|
40
|
+
static builder(): AgentBuilder;
|
|
41
|
+
}
|
|
42
|
+
declare class AgentBuilder {
|
|
43
|
+
_name: string;
|
|
44
|
+
_systemPrompt?: string;
|
|
45
|
+
_tools: Tool[];
|
|
46
|
+
_model: Model;
|
|
47
|
+
_conversation: Conversation | undefined;
|
|
48
|
+
_conversationManager: ConversationManager;
|
|
49
|
+
_toolNameToIndexMap: any | undefined;
|
|
50
|
+
_callback: (m: Message) => void;
|
|
51
|
+
name(name: string): AgentBuilder;
|
|
52
|
+
systemPrompt(systemPrompt: string | undefined): AgentBuilder;
|
|
53
|
+
tools(tools: Tool[]): AgentBuilder;
|
|
54
|
+
addTool(tool: Tool): AgentBuilder;
|
|
55
|
+
model(model: Model): AgentBuilder;
|
|
56
|
+
conversationManager(mgr: ConversationManager): AgentBuilder;
|
|
57
|
+
callback(callback: (m: Message) => void): AgentBuilder;
|
|
58
|
+
build(): Agent;
|
|
59
|
+
}
|
|
60
|
+
export declare function defaultCallback(m: Message): void;
|
|
61
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Message } from "..";
|
|
2
|
+
import type { Model } from "../model/model";
|
|
3
|
+
/**
|
|
4
|
+
* Maintains ordered chat messages and delegates to a conversation manager for compaction.
|
|
5
|
+
*/
|
|
6
|
+
export default class Conversation {
|
|
7
|
+
messages: Message[];
|
|
8
|
+
manager: ConversationManager;
|
|
9
|
+
/**
|
|
10
|
+
* Initialize an empty conversation using the default manager.
|
|
11
|
+
*/
|
|
12
|
+
constructor();
|
|
13
|
+
/**
|
|
14
|
+
* Append a message to the history and apply compaction rules.
|
|
15
|
+
* @param message Message to store.
|
|
16
|
+
*/
|
|
17
|
+
append(message: Message): void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Strategy interface for pruning or transforming a conversation history.
|
|
21
|
+
*/
|
|
22
|
+
export declare class ConversationManager {
|
|
23
|
+
/**
|
|
24
|
+
* Compact or adjust a conversation according to a strategy.
|
|
25
|
+
* @param conversation Full list of messages so far.
|
|
26
|
+
* @returns Adjusted message list to keep.
|
|
27
|
+
*/
|
|
28
|
+
compact(_conversation: Message[]): Message[];
|
|
29
|
+
/**
|
|
30
|
+
* Allow the manager to consider model-specific constraints (e.g., token limits).
|
|
31
|
+
* @param model Model being used for this conversation.
|
|
32
|
+
*/
|
|
33
|
+
configure(_model: Model): void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Default conversation manager that leaves all messages intact.
|
|
37
|
+
*/
|
|
38
|
+
export declare class DefaultConversationManager extends ConversationManager {
|
|
39
|
+
/**
|
|
40
|
+
* Default strategy simply returns the conversation untouched.
|
|
41
|
+
*/
|
|
42
|
+
compact(conversation: Message[]): Message[];
|
|
43
|
+
/**
|
|
44
|
+
* Default manager ignores model configuration because it does no compaction.
|
|
45
|
+
*/
|
|
46
|
+
configure(_model: Model): void;
|
|
47
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import type ToolCall from "../tool/toolCall";
|
|
2
|
+
/**
|
|
3
|
+
* Messages are the units of a Conversation
|
|
4
|
+
* They consist of a series of MessageComponents, several of which are already defined by Tapeworm
|
|
5
|
+
* and can be extended to be used for other use cases.
|
|
6
|
+
*/
|
|
7
|
+
export default class Message {
|
|
8
|
+
role: string;
|
|
9
|
+
content: MessageComponent[];
|
|
10
|
+
/**
|
|
11
|
+
* Wrap a set of message components with an associated sender role.
|
|
12
|
+
* @param role Source of the message (e.g., user, assistant, tool).
|
|
13
|
+
* @param content Ordered list of message components that make up the message body.
|
|
14
|
+
*/
|
|
15
|
+
constructor(role: string, content: MessageComponent[]);
|
|
16
|
+
/**
|
|
17
|
+
* Create a new message builder for ergonomic construction.
|
|
18
|
+
* @returns A MessageBuilder instance.
|
|
19
|
+
*/
|
|
20
|
+
static builder(): MessageBuilder;
|
|
21
|
+
/**
|
|
22
|
+
* Filters the message contents based on the message type.
|
|
23
|
+
*/
|
|
24
|
+
filter(type: MessageComponentType): MessageComponent[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Tapeworm prefers builders rather than constructors.
|
|
28
|
+
*
|
|
29
|
+
* This builder provides ergonomic methods to construct a message w/ its message components.
|
|
30
|
+
*
|
|
31
|
+
*/
|
|
32
|
+
export declare class MessageBuilder {
|
|
33
|
+
private _role;
|
|
34
|
+
private _content;
|
|
35
|
+
/**
|
|
36
|
+
* Set the role associated with the message being built.
|
|
37
|
+
* @param role Source of the message (e.g., user, assistant, tool).
|
|
38
|
+
* @returns This builder for chaining.
|
|
39
|
+
*/
|
|
40
|
+
role(role: string): MessageBuilder;
|
|
41
|
+
/**
|
|
42
|
+
* Ensure the content array is initialized before appending components.
|
|
43
|
+
*/
|
|
44
|
+
init(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Attach a tool call component if provided.
|
|
47
|
+
* @param toolCall Tool call to append to the message.
|
|
48
|
+
* @returns This builder for chaining.
|
|
49
|
+
*/
|
|
50
|
+
toolCall(toolCall: ToolCall | undefined): MessageBuilder;
|
|
51
|
+
/**
|
|
52
|
+
* Attach SEVERAL tool call component if provided.
|
|
53
|
+
* @param toolCall Tool call to append to the message.
|
|
54
|
+
* @returns This builder for chaining.
|
|
55
|
+
*/
|
|
56
|
+
toolCalls(toolCalls: ToolCall[] | undefined): MessageBuilder;
|
|
57
|
+
/**
|
|
58
|
+
* Attach a tool result component if provided.
|
|
59
|
+
* @param toolResult Result of a previously executed tool call.
|
|
60
|
+
* @returns This builder for chaining.
|
|
61
|
+
*/
|
|
62
|
+
toolResult(toolResult: ToolResult | undefined): MessageBuilder;
|
|
63
|
+
/**
|
|
64
|
+
* Attach a thinking component if provided.
|
|
65
|
+
* @param thinking Reflection or chain-of-thought text.
|
|
66
|
+
* @returns This builder for chaining.
|
|
67
|
+
*/
|
|
68
|
+
thinking(thinking: string | undefined): MessageBuilder;
|
|
69
|
+
/**
|
|
70
|
+
* Attach content to the message if provided.
|
|
71
|
+
* @param content Assistant or user text content.
|
|
72
|
+
* @returns This builder for chaining.
|
|
73
|
+
*/
|
|
74
|
+
content(content: string | undefined): MessageBuilder;
|
|
75
|
+
/**
|
|
76
|
+
* Construct the Message with the accumulated values.
|
|
77
|
+
* @returns Finalized Message instance.
|
|
78
|
+
*/
|
|
79
|
+
build(): Message;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* These are the official MessageComponent types supported by Tapeworm.
|
|
83
|
+
*
|
|
84
|
+
* Tapeworm guarantees that these can all be processed by officially supported models.
|
|
85
|
+
*/
|
|
86
|
+
export declare const MessageComponentType: {
|
|
87
|
+
Content: string;
|
|
88
|
+
Thinking: string;
|
|
89
|
+
ToolCall: string;
|
|
90
|
+
ToolResult: string;
|
|
91
|
+
};
|
|
92
|
+
export type MessageComponentType = (typeof MessageComponentType)[keyof typeof MessageComponentType];
|
|
93
|
+
/**
|
|
94
|
+
* This is the base class of a message component.
|
|
95
|
+
* It must override the getMessageComponentType function.
|
|
96
|
+
*/
|
|
97
|
+
export declare class MessageComponent {
|
|
98
|
+
/**
|
|
99
|
+
* Get the type of this message component, as defined in MessageComponentType
|
|
100
|
+
*/
|
|
101
|
+
getMessageComponentType(): MessageComponentType;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* A Content message block, representing text from the LLM.
|
|
105
|
+
* It contains only content.
|
|
106
|
+
* It must override the getMessageComponentType function.
|
|
107
|
+
*/
|
|
108
|
+
export declare class Content extends MessageComponent {
|
|
109
|
+
text: string;
|
|
110
|
+
/**
|
|
111
|
+
* Create a content component with the provided text.
|
|
112
|
+
* @param text Body of the content block.
|
|
113
|
+
*/
|
|
114
|
+
constructor(text: string);
|
|
115
|
+
/**
|
|
116
|
+
* Get the type of this message component, as defined in MessageComponentType.
|
|
117
|
+
* This returns MessageComponentType.Content.
|
|
118
|
+
*/
|
|
119
|
+
getMessageComponentType(): MessageComponentType;
|
|
120
|
+
/**
|
|
121
|
+
* Get the content of this MessageComponent.
|
|
122
|
+
* @returns the string contained in this component.
|
|
123
|
+
*/
|
|
124
|
+
get(): string;
|
|
125
|
+
/**
|
|
126
|
+
* Shorthand for the constructor
|
|
127
|
+
* @param text - the text this content block represents.
|
|
128
|
+
* @returns a new Content messagecomponent with the text.
|
|
129
|
+
*/
|
|
130
|
+
static of(text: string): Content;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* A Thinking message block, representing a thought from the LLM.
|
|
134
|
+
* It contains only a single thought, much like my brain as I'm writing this.
|
|
135
|
+
* It must override the getMessageComponentType function.
|
|
136
|
+
*/
|
|
137
|
+
export declare class Thinking extends MessageComponent {
|
|
138
|
+
thought: string;
|
|
139
|
+
/**
|
|
140
|
+
* Create a thinking component with the provided thought text.
|
|
141
|
+
* @param thought Chain-of-thought or reasoning string.
|
|
142
|
+
*/
|
|
143
|
+
constructor(thought: string);
|
|
144
|
+
/**
|
|
145
|
+
* Get the type of this message component, as defined in MessageComponentType.
|
|
146
|
+
* This returns MessageComponentType.Content.
|
|
147
|
+
*/
|
|
148
|
+
getMessageComponentType(): MessageComponentType;
|
|
149
|
+
/**
|
|
150
|
+
* Get the content of this MessageComponent.
|
|
151
|
+
* @returns the string contained in this component.
|
|
152
|
+
*/
|
|
153
|
+
get(): string;
|
|
154
|
+
/**
|
|
155
|
+
* Shorthand for the constructor
|
|
156
|
+
* @param thought - the text this content block represents.
|
|
157
|
+
* @returns a new Thinking message component with the text.
|
|
158
|
+
*/
|
|
159
|
+
static of(thought: string): Thinking;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* A Thinking message block, representing a thought from the LLM.
|
|
163
|
+
* It contains only a single thought, much like my brain as I'm writing this.
|
|
164
|
+
* It must override the getMessageComponentType function.
|
|
165
|
+
*/
|
|
166
|
+
export declare class ToolResult extends MessageComponent {
|
|
167
|
+
id: string;
|
|
168
|
+
toolName: string;
|
|
169
|
+
toolResult: any;
|
|
170
|
+
/**
|
|
171
|
+
* Create a tool result component for a completed tool call.
|
|
172
|
+
* @param id Identifier of the originating tool call.
|
|
173
|
+
* @param toolName Name of the tool that produced the result.
|
|
174
|
+
* @param toolResult Output returned by the tool.
|
|
175
|
+
*/
|
|
176
|
+
constructor(id: string, toolName: string, toolResult: any);
|
|
177
|
+
/**
|
|
178
|
+
* Get the type of this message component, as defined in MessageComponentType.
|
|
179
|
+
* This returns MessageComponentType.ToolResult.
|
|
180
|
+
*/
|
|
181
|
+
getMessageComponentType(): MessageComponentType;
|
|
182
|
+
/**
|
|
183
|
+
* Shorthand for the constructor
|
|
184
|
+
* @param toolCall Original tool call that produced this result.
|
|
185
|
+
* @param toolResult Output to attach to the tool result component.
|
|
186
|
+
* @returns a new ToolResult component mirroring the provided tool call.
|
|
187
|
+
*/
|
|
188
|
+
static of(toolCall: ToolCall, toolResult: any): ToolResult;
|
|
189
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { default as Agent } from "./agent/agent";
|
|
2
|
+
export { default as Conversation, ConversationManager, DefaultConversationManager, } from "./conversation/conversation";
|
|
3
|
+
export { Model, ModelRequest, ModelRequestBuilder } from "./model/model";
|
|
4
|
+
export { default as Tool } from "./tool/tool";
|
|
5
|
+
export { default as ToolCall, ToolCallBuilder } from "./tool/toolCall";
|
|
6
|
+
export { default as ToolSchema, ToolSchemaBuilder, Parameter, ParameterBuilder, } from "./tool/toolschema";
|
|
7
|
+
export { default as OllamaModel } from "./model/OllamaModel";
|
|
8
|
+
export { default as Message, MessageBuilder, MessageComponentType, MessageComponent, Content, Thinking, ToolResult, } from "./conversation/message";
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Message } from "..";
|
|
2
|
+
import ToolCall from "../tool/toolCall";
|
|
3
|
+
import { Model, ModelRequest } from "./model";
|
|
4
|
+
/**
|
|
5
|
+
* Model adapter that translates between Tapeworm message/tool shapes and the Ollama chat API.
|
|
6
|
+
*/
|
|
7
|
+
export default class OllamaModel extends Model {
|
|
8
|
+
endpoint: string;
|
|
9
|
+
model: string;
|
|
10
|
+
options?: any;
|
|
11
|
+
/**
|
|
12
|
+
* Create a model wrapper for an Ollama chat endpoint.
|
|
13
|
+
* @param endpoint Base URL of the Ollama server (e.g., http://localhost:11434).
|
|
14
|
+
* @param model Model name/tag to use for inference.
|
|
15
|
+
* @param options Additional Ollama options passed through to the API.
|
|
16
|
+
*/
|
|
17
|
+
constructor(endpoint: string, model: string, options: any);
|
|
18
|
+
/**
|
|
19
|
+
* Call the Ollama chat API with the provided conversation and tools.
|
|
20
|
+
* Formats the request, performs the HTTP POST, and translates the response into a ModelResponse.
|
|
21
|
+
*/
|
|
22
|
+
invoke(request: ModelRequest): Promise<Message>;
|
|
23
|
+
/**
|
|
24
|
+
* Convert internal tool definitions into the JSON schema format expected by Ollama.
|
|
25
|
+
* @param request Model request containing declared tools.
|
|
26
|
+
* @returns Array of tool schema objects formatted for the Ollama API.
|
|
27
|
+
*/
|
|
28
|
+
_formatTools(request: ModelRequest): any;
|
|
29
|
+
/**
|
|
30
|
+
* Convert internal message objects into Ollama's chat message shape.
|
|
31
|
+
* Passes through assistant/system/user roles and maps tool role into the expected structure.
|
|
32
|
+
* @param request Model request containing the message history.
|
|
33
|
+
* @returns Array of serialized messages ready for Ollama.
|
|
34
|
+
*/
|
|
35
|
+
_formatMessages(request: ModelRequest): any;
|
|
36
|
+
/**
|
|
37
|
+
* Format everything sent by a user or LLM (no tools)
|
|
38
|
+
* @param message Message to serialize for Ollama.
|
|
39
|
+
* @returns Serialized message payload.
|
|
40
|
+
*/
|
|
41
|
+
_formatSingleMessage(message: Message): any;
|
|
42
|
+
/**
|
|
43
|
+
* Convert a ToolCall into the structure Ollama expects.
|
|
44
|
+
* @param toolCall ToolCall to serialize.
|
|
45
|
+
* @returns Minimal representation containing function name and arguments.
|
|
46
|
+
*/
|
|
47
|
+
_formatToolCall(toolCall: ToolCall): any;
|
|
48
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type Message from "../conversation/message";
|
|
2
|
+
import type Tool from "../tool/tool";
|
|
3
|
+
/**
|
|
4
|
+
* A Model represents the LLM backend for the agent.
|
|
5
|
+
*/
|
|
6
|
+
export declare class Model {
|
|
7
|
+
/**
|
|
8
|
+
* Execute the model against the provided request.
|
|
9
|
+
* @param request Aggregated messages and tools to send to the model.
|
|
10
|
+
*/
|
|
11
|
+
invoke(_request: ModelRequest): Promise<Message>;
|
|
12
|
+
/**
|
|
13
|
+
* Return the maximum token budget supported by this model.
|
|
14
|
+
*/
|
|
15
|
+
tokenLimit(): void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Container for the messages and tools required to invoke a model.
|
|
19
|
+
*/
|
|
20
|
+
export declare class ModelRequest {
|
|
21
|
+
messages: Message[];
|
|
22
|
+
tools: Tool[];
|
|
23
|
+
/**
|
|
24
|
+
* Create a new model request envelope.
|
|
25
|
+
* @param messages Conversation history to send to the model.
|
|
26
|
+
* @param tools Tool definitions available for function calling.
|
|
27
|
+
*/
|
|
28
|
+
constructor(messages: Message[], tools: Tool[]);
|
|
29
|
+
/**
|
|
30
|
+
* Create a builder for composing a model request.
|
|
31
|
+
* @returns A ModelRequestBuilder instance.
|
|
32
|
+
*/
|
|
33
|
+
static builder(): ModelRequestBuilder;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Fluent builder for constructing ModelRequest instances.
|
|
37
|
+
*/
|
|
38
|
+
export declare class ModelRequestBuilder {
|
|
39
|
+
private _messages;
|
|
40
|
+
private _tools;
|
|
41
|
+
/**
|
|
42
|
+
* Supply the conversation messages to include.
|
|
43
|
+
* @returns This builder for chaining.
|
|
44
|
+
*/
|
|
45
|
+
messages(messages: Message[]): ModelRequestBuilder;
|
|
46
|
+
/**
|
|
47
|
+
* Supply the tool list for this request.
|
|
48
|
+
* @returns This builder for chaining.
|
|
49
|
+
*/
|
|
50
|
+
tools(tools: Tool[]): ModelRequestBuilder;
|
|
51
|
+
/**
|
|
52
|
+
* Build the ModelRequest, defaulting tools to an empty array.
|
|
53
|
+
* @returns Constructed ModelRequest.
|
|
54
|
+
*/
|
|
55
|
+
build(): ModelRequest;
|
|
56
|
+
}
|
package/dist/tapeworm.cjs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class _{messages;manager;constructor(){this.messages=[],this.manager=new T}append(e){this.messages.push(e),this.messages=this.manager.compact(this.messages)}}class a{role;content;toolCalls;toolName;thinking;constructor(e,s,t,o,r){this.role=e,this.content=s,this.toolCalls=t,this.toolName=o,this.thinking=r}setRole(e){this.role=e}setContent(e){this.content=e}setToolCalls(e){this.toolCalls=e}static builder(){return new f}}class f{_role;_content;_toolCalls;_toolName;_thinking;role(e){return this._role=e,this}content(e){return this._content=e,this}toolCalls(e){return this._toolCalls=e,this}toolName(e){return this._toolName=e,this}thinking(e){return this._thinking=e,this}build(){return new a(this._role,this._content,this._toolCalls,this._toolName,this._thinking)}}class y{compact(e){throw new p("No implementation for conversation manager!")}configure(e){throw new p("No implementation for conversation manager!")}}class T extends y{compact(e){return e}configure(e){}}class p extends Error{constructor(e){super(e),this.name="ConversationManagerNotImplemented"}}class v{async invoke(e){throw new g("The invoke function for this model was not correctly implemented.")}tokenLimit(){throw new g("The tokenLimit function for this model was not correctly implemented.")}}class w{messages;tools;constructor(e,s){this.messages=e,this.tools=s}static builder(){return new u}}class u{_messages;_tools;messages(e){return this._messages=e,this}tools(e){return this._tools=e,this}build(){return this._tools==null&&(this._tools=[]),new w(this._messages,this._tools)}}class b{toolCalls;role;content;thinking;constructor(e,s,t,o){this.toolCalls=e,this.role=s,this.content=t,this.thinking=o}static builder(){return new m}}class m{_toolCalls;_role;_content;_thinking;toolCalls(e){return this._toolCalls=e,this}role(e){return this._role=e,this}content(e){return this._content=e,this}thinking(e){return this._thinking=e,this}build(){return new b(this._toolCalls,this._role,this._content,this._thinking)}}class g extends Error{constructor(e){super(e),this.name="ModelNotImplementedError"}}class B{name;system_prompt;tools;model;conversation;conversationManager;toolNameToIndexMap;async invoke(e){this.conversation==null&&(this.conversation=new _,this.conversationManager!=null&&(this.conversation.manager=this.conversationManager),this.conversation.append(a.builder().role("system").content(this.system_prompt).build())),this.conversation.append(a.builder().role("user").content(e).build());let s=!1;for(;!s;){let t=await this._runQuery();if(this.conversation.append(a.builder().role(t.role??"assistant").content(t.content??"").thinking(t.thinking??"").build()),console.log("Assistant thinking: "+t.thinking),console.log("Assistant reply: "+t.content),s=!0,t.toolCalls!=null&&t.toolCalls.length!=0){s=!1,t.toolCalls.sort((o,r)=>(o.sequence??0)<(r.sequence??0)?-1:1);for(let o in t.toolCalls)this._runTool(t.toolCalls[o])}}}async _runQuery(){return await this.model.invoke(new u().messages(this.conversation?.messages).tools(this.tools).build())}async _runTool(e){if(console.log("Calling tool: "+JSON.stringify(e)),this.generateToolNameToIndexMap(),!(e.name in this.toolNameToIndexMap)){this.conversation.append(a.builder().role("tool").toolName(e.name).content(JSON.stringify({error:"tool does not exist"})).build());return}let s=this.tools[this.toolNameToIndexMap[e.name]];try{let t=s.execute(e.parameters);this.conversation.append(a.builder().role("tool").toolName(e.name).content(JSON.stringify(t)).build())}catch(t){this.conversation.append(a.builder().role("tool").toolName(e.name).content(JSON.stringify(t)).build())}}generateToolNameToIndexMap(){if(this.toolNameToIndexMap==null){this.toolNameToIndexMap={};for(let e=0;e<this.tools.length;e++)this.toolNameToIndexMap[this.tools[e].getName()]=e}}}class c extends Error{constructor(e){super(e),this.name="ToolNotDefinedError"}}class I{name;description;tool_schema;constructor(){this.name=this.getName(),this.description=this.getDescription(),this.tool_schema=this.getToolSchema()}getName(){throw new c("Tool name not defined.")}getDescription(){throw new c("Tool description not defined.")}getToolSchema(){throw new c("Tool parameter schema not defined.")}execute(e){return null}}class d{sequence;name;parameters;type;constructor(e,s,t,o){this.sequence=e,this.name=s,this.parameters=t,this.type=o}static builder(){return new M}}class M{_sequence;_name;_parameters;_type;sequence(e){return this._sequence=e,this}name(e){return this._name=e,this}parameters(e){return this._parameters=e,this}type(e){return this._type=e,this}build(){return this._sequence==null&&(this._sequence=0),new d(this._sequence,this._name,this._parameters,this._type)}}class N{parameters;output;constructor(e,s){this.parameters=e,this.output=s}static builder(){return new C}}class C{_parameters;_output;addParameter(e){return this._parameters==null&&(this._parameters=[]),this._parameters.push(e),this}output(e){return this._output=e,this}build(){return this._parameters==null&&(this._parameters=[]),new N(this._parameters,this._output)}}class q{name;description;type;required;constructor(e,s,t,o){this.name=e,this.description=s,this.type=t,this.required=o,this.assertValidType()}assertValidType(){}static builder(){return new k}}class k{_name;_description;_type;_required;name(e){return this._name=e,this}description(e){return this._description=e,this}type(e){return this._type=e,this}required(e){return this._required=e,this}build(){return this._required==null&&(this._required=!1),new q(this._name,this._description,this._type,this._required)}}class j extends v{endpoint;model;options;constructor(e,s,t){super(),this.endpoint=e,this.model=s,this.options=t}async invoke(e){let s={model:this.model,messages:this._formatMessages(e),tools:this._formatTools(e),...this.options},t=await fetch(this.endpoint+"/api/chat",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});if(!t.ok)throw new Error(`HTTP error! status: ${t.status}`);let o=await t.json(),r=[];if(o.message.tool_calls)for(let h in o.message.tool_calls){const i=o.message.tool_calls[h];let l="function",x=i[l]?.name,O=i[l]?.index,S=i[l]?.arguments;r.push(d.builder().name(x).type(l).parameters(S).sequence(O).build())}return new m().toolCalls(r).role(o.message.role).content(o.message.content).thinking(o.message.thinking).build()}_formatTools(e){let s=[],t=[];for(let o of e.tools){let r={};for(let i of o.getToolSchema().parameters)r[i.name]={},r[i.name].type=i.name,r[i.name].description=i.description,i.required&&t.push(i.name);let h={type:"function",function:{name:o.getName(),description:o.getDescription(),parameters:{type:"object",properties:r,required:t}}};s.push(h)}return s}_formatMessages(e){let s=[];for(let t of e.messages){if(t.role=="assistant"||t.role=="system"||t.role=="user"){s.push(t);continue}t.role=="tool"&&s.push({role:"tool",content:t.content,tool_name:t.toolName})}return s}}exports.Agent=B;exports.Conversation=_;exports.ConversationManager=y;exports.DefaultConversationManager=T;exports.Message=a;exports.MessageBuilder=f;exports.Model=v;exports.ModelRequest=w;exports.ModelRequestBuilder=u;exports.ModelResponse=b;exports.ModelResponseBuilder=m;exports.OllamaModel=j;exports.Parameter=q;exports.ParameterBuilder=k;exports.Tool=I;exports.ToolCall=d;exports.ToolCallBuilder=M;exports.ToolSchema=N;exports.ToolSchemaBuilder=C;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class v{messages;manager;constructor(){this.messages=[],this.manager=new p}append(e){this.messages.push(e),this.messages=this.manager.compact(this.messages)}}class y{compact(e){throw new _("No implementation for conversation manager!")}configure(e){throw new _("No implementation for conversation manager!")}}class p extends y{compact(e){return e}configure(e){}}class _ extends Error{constructor(e){super(e),this.name="ConversationManagerNotImplemented"}}class h{role;content;constructor(e,t){this.role=e,this.content=t}static builder(){return new M}filter(e){return this.content.filter(t=>t.getMessageComponentType()==e)}}class M{_role;_content;role(e){return this._role=e,this}init(){this._content==null&&(this._content=[])}toolCall(e){return e==null?this:(this.init(),this._content.push(e),this)}toolCalls(e){if(e==null)return this;this.init();for(const t of e)this._content.push(t);return this}toolResult(e){return e==null?this:(this.init(),this._content.push(e),this)}thinking(e){return e==null?this:(this.init(),this._content.push(C.of(e)),this)}content(e){return e==null?this:(this.init(),this._content.push(w.of(e)),this)}build(){if(this._content==null||this._content.length==0)throw new Error("Role-only messages are not supported by Tapeworm.");return new h(this._role,this._content)}}const a={Content:"content",Thinking:"thinking",ToolCall:"toolcall",ToolResult:"toolresult"};class u{getMessageComponentType(){throw new Error("Message components that do not have a message component type are not allowed.")}}class w extends u{text;constructor(e){super(),this.text=e}getMessageComponentType(){return a.Content}get(){return this.text}static of(e){return new this(e)}}class C extends u{thought;constructor(e){super(),this.thought=e}getMessageComponentType(){return a.Thinking}get(){return this.thought}static of(e){return new this(e)}}class m extends u{id;toolName;toolResult;constructor(e,t,o){super(),this.id=e,this.toolName=t,this.toolResult=o}getMessageComponentType(){return a.ToolResult}static of(e,t){return new this(e.id,e.name,t)}}class b{async invoke(e){throw new T("The invoke function for this model was not correctly implemented.")}tokenLimit(){throw new T("The tokenLimit function for this model was not correctly implemented.")}}class N{messages;tools;constructor(e,t){this.messages=e,this.tools=t}static builder(){return new g}}class g{_messages;_tools;messages(e){return this._messages=e,this}tools(e){return this._tools=e,this}build(){if(this._tools==null&&(this._tools=[]),this._messages==null)throw new j("Requests to the model should include content.");return new N(this._messages,this._tools)}}class T extends Error{constructor(e){super(e),this.name="ModelNotImplementedError"}}class j extends Error{constructor(e){super(e),this.name="MessagesNotDefinedError"}}class f extends u{sequence;name;parameters;type;id;getMessageComponentType(){return a.ToolCall}constructor(e,t,o,s,i){super(),this.sequence=e,this.name=t,this.parameters=o,this.type=s,this.id=i}static builder(){return new x}}class x{_sequence;_name;_parameters;_type;_id;sequence(e){return this._sequence=e,this}name(e){return this._name=e,this}parameters(e){return this._parameters=e,this}type(e){return this._type=e,this}id(e){return e!=null&&(this._id=e),this}build(){return this._sequence==null&&(this._sequence=0),this._id==null&&(this._id=(Math.random()+1).toString(36).slice(2,7)),new f(this._sequence,this._name,this._parameters,this._type,this._id)}}class D extends Error{constructor(e){super(e),this.name="ToolNotFoundError"}}class k{name;systemPrompt;tools;model;conversation;conversationManager;toolNameToIndexMap;callback;constructor(e,t,o,s,i){this.name=e,this.model=o,this.conversationManager=s,this.tools=t,this.callback=i,this.conversationManager.configure(o)}async invoke(e){this.conversation==null&&(this.conversation=new v,this.conversationManager!=null&&(this.conversation.manager=this.conversationManager),this.systemPrompt!=null&&this.conversation.append(h.builder().role("system").content(this.systemPrompt).build())),this.conversation.append(h.builder().role("user").content(e).build());let t=!1;for(;!t;){let o=await this._runQuery();this.callback(o),this.conversation.append(o),t=!0;const s=o.filter(a.ToolCall);if(s!=null&&s.length!=0){t=!1,s.sort((i,l)=>(i.sequence??0)<(l.sequence??0)?-1:1);for(let i of s)await this._runTool(i)}}}async _runQuery(){return await this.model.invoke(new g().messages(this.conversation?.messages).tools(this.tools).build())}async _runTool(e){if(this.generateToolNameToIndexMap(),!(e.name in this.toolNameToIndexMap)){this.conversation.append(h.builder().role("tool").toolResult(m.of(e,new D("Agent does not have a tool with this name."))).build());return}let t=this.tools[this.toolNameToIndexMap[e.name]];try{let o=await t.execute(e.parameters);this.conversation.append(h.builder().role("tool").toolResult(m.of(e,o)).build())}catch(o){this.conversation.append(h.builder().role("tool").toolResult(m.of(e,JSON.stringify(o))).build())}}generateToolNameToIndexMap(){if(this.toolNameToIndexMap==null){this.toolNameToIndexMap={};for(let e=0;e<this.tools.length;e++)this.toolNameToIndexMap[this.tools[e].getName()]=e}}static builder(){return new A}}class A{_name;_systemPrompt;_tools;_model;_conversation;_conversationManager=new p;_toolNameToIndexMap;_callback=e=>J(e);name(e){return this._name=e,this}systemPrompt(e){return this._systemPrompt=e,this}tools(e){return this._tools=e,this}addTool(e){return this._tools==null&&(this._tools=[]),this._tools.push(e),this}model(e){return this._model=e,this}conversationManager(e){return this._conversationManager=e,this}callback(e){return this._callback=e,this}build(){let e=new k(this._name,this._tools,this._model,this._conversationManager,this._callback);return this._conversation!=null&&(e.conversation=this._conversation),this._systemPrompt!=null&&(e.systemPrompt=this._systemPrompt),e}}function J(n){for(const e of n.filter(a.Thinking))console.log("\x1B[90m"+e.get()+"\x1B[0m");for(const e of n.filter(a.Content))console.log(e.get());for(const e of n.filter(a.ToolCall))console.log("\x1B[32mCalling Tool: "+e.name+"\x1B[0m")}class d extends Error{constructor(e){super(e),this.name="ToolNotDefinedError"}}class F{name;description;tool_schema;constructor(){this.name=this.getName(),this.description=this.getDescription(),this.tool_schema=this.getToolSchema()}getName(){throw new d("Tool name not defined.")}getDescription(){throw new d("Tool description not defined.")}getToolSchema(){throw new d("Tool parameter schema not defined.")}execute(e){return null}}class q{parameters;output;constructor(e,t){this.parameters=e,this.output=t}static builder(){return new R}}class R{_parameters;_output;addParameter(e){return this._parameters==null&&(this._parameters=[]),this._parameters.push(e),this}output(e){return this._output=e,this}build(){return this._parameters==null&&(this._parameters=[]),new q(this._parameters,this._output)}}class E{name;description;type;required;constructor(e,t,o,s){this.name=e,this.description=t,this.type=o,this.required=s,this.assertValidType()}assertValidType(){}static builder(){return new P}}class P{_name;_description;_type;_required;name(e){return this._name=e,this}description(e){return this._description=e,this}type(e){return this._type=e,this}required(e){return this._required=e,this}build(){return this._required==null&&(this._required=!1),new E(this._name,this._description,this._type,this._required)}}class L extends b{endpoint;model;options;constructor(e,t,o){super(),this.endpoint=e,this.model=t,this.options=o}async invoke(e){let t={model:this.model,messages:this._formatMessages(e),tools:this._formatTools(e),...this.options},o=await fetch(this.endpoint+"/api/chat",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)});if(!o.ok)throw new Error(`HTTP error! status: ${o.status}`);let s=await o.json(),i=[];if(s.message.tool_calls)for(let l in s.message.tool_calls){const r=s.message.tool_calls[l];let c="function",S=r[c]?.name,B=r[c]?.index,O=r.id??void 0,I=typeof r[c]?.arguments=="string"?JSON.parse(r[c]?.arguments):r[c]?.arguments;i.push(f.builder().name(S).type(c).parameters(I).sequence(B).id(O).build())}return h.builder().toolCalls(i).role(s.message.role).content(s.message.content).thinking(s.message.thinking).build()}_formatTools(e){let t=[];for(let o of e.tools){let s={},i=[];for(let r of o.getToolSchema().parameters)s[r.name]={},s[r.name].type=r.type,s[r.name].description=r.description,r.required&&i.push(r.name);let l={type:"function",function:{name:o.getName(),description:o.getDescription(),parameters:{type:"object",properties:s,required:i}}};t.push(l)}return t}_formatMessages(e){let t=[];for(let o of e.messages){if(o.role=="assistant"||o.role=="system"||o.role=="user"){t.push(this._formatSingleMessage(o));continue}if(o.role=="tool"){for(const s of o.content)if(s.getMessageComponentType()==a.ToolResult){const i=s;t.push({role:o.role,name:i.toolName,content:JSON.stringify(i.toolResult)})}}}return t}_formatSingleMessage(e){let t={role:e.role},o,s,i;for(const l of e.content){if(l.getMessageComponentType()==a.Content){const r=l;o==null&&(o=""),o+=r.get()}if(l.getMessageComponentType()==a.Thinking){const r=l;i==null&&(i=""),i+=r.get()}if(l.getMessageComponentType()==a.ToolCall){const r=l;s==null&&(s=[]),s.push(this._formatToolCall(r))}}return o!=null&&(t.content=o),s!=null&&(t.tool_calls=s),i!=null&&(t.thinking=i),t}_formatToolCall(e){return{function:{name:e.name,arguments:e.parameters}}}}exports.Agent=k;exports.Content=w;exports.Conversation=v;exports.ConversationManager=y;exports.DefaultConversationManager=p;exports.Message=h;exports.MessageBuilder=M;exports.MessageComponent=u;exports.MessageComponentType=a;exports.Model=b;exports.ModelRequest=N;exports.ModelRequestBuilder=g;exports.OllamaModel=L;exports.Parameter=E;exports.ParameterBuilder=P;exports.Thinking=C;exports.Tool=F;exports.ToolCall=f;exports.ToolCallBuilder=x;exports.ToolResult=m;exports.ToolSchema=q;exports.ToolSchemaBuilder=R;
|