@morphllm/subagents 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/README.md +146 -0
- package/dist/core/index.d.mts +68 -0
- package/dist/core/index.d.ts +68 -0
- package/dist/core/index.js +221 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/index.mjs +218 -0
- package/dist/core/index.mjs.map +1 -0
- package/dist/docx/index.d.mts +110 -0
- package/dist/docx/index.d.ts +110 -0
- package/dist/docx/index.js +552 -0
- package/dist/docx/index.js.map +1 -0
- package/dist/docx/index.mjs +549 -0
- package/dist/docx/index.mjs.map +1 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +554 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +549 -0
- package/dist/index.mjs.map +1 -0
- package/dist/pdf/index.d.mts +17 -0
- package/dist/pdf/index.d.ts +17 -0
- package/dist/pdf/index.js +10 -0
- package/dist/pdf/index.js.map +1 -0
- package/dist/pdf/index.mjs +7 -0
- package/dist/pdf/index.mjs.map +1 -0
- package/dist/types-BEtL6Wum.d.ts +48 -0
- package/dist/types-D9rS2MQq.d.mts +82 -0
- package/dist/types-D9rS2MQq.d.ts +82 -0
- package/dist/types-DBtV_NyH.d.mts +48 -0
- package/package.json +71 -0
- package/src/core/base-agent.ts +230 -0
- package/src/core/base-client.ts +93 -0
- package/src/core/index.ts +17 -0
- package/src/core/types.ts +64 -0
- package/src/docx/agent.ts +311 -0
- package/src/docx/client.ts +69 -0
- package/src/docx/index.ts +18 -0
- package/src/docx/types.ts +68 -0
- package/src/index.ts +66 -0
- package/src/pdf/index.ts +23 -0
- package/src/pdf/types.ts +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# @morphllm/subagents
|
|
2
|
+
|
|
3
|
+
Modular AI subagents for document processing. Built for use with OpenAI-compatible APIs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @morphllm/subagents
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Available Subagents
|
|
12
|
+
|
|
13
|
+
| Subagent | Description | Status |
|
|
14
|
+
|----------|-------------|--------|
|
|
15
|
+
| **DOCX** | Track changes, comments, and document editing | Available |
|
|
16
|
+
| **PDF** | Form filling and annotations | Coming Soon |
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
### DOCX Client (Direct API Access)
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { DocxClient } from '@morphllm/subagents/docx';
|
|
24
|
+
|
|
25
|
+
const client = new DocxClient();
|
|
26
|
+
|
|
27
|
+
// Upload a document
|
|
28
|
+
const doc = await client.upload(file);
|
|
29
|
+
|
|
30
|
+
// Read content
|
|
31
|
+
const content = await client.read(doc.doc_id);
|
|
32
|
+
|
|
33
|
+
// Add comments and track changes
|
|
34
|
+
await client.edit(doc.doc_id, [
|
|
35
|
+
{ type: 'add_comment', para_text: 'Introduction', comment: 'Needs citation' },
|
|
36
|
+
{ type: 'insert_text', para_text: 'Introduction', after: '.', new_text: ' [1]' },
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
// Download the edited document
|
|
40
|
+
const blob = await client.download(doc.doc_id);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### DOCX Agent (AI-Powered)
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import OpenAI from 'openai';
|
|
47
|
+
import { DocxAgent } from '@morphllm/subagents/docx';
|
|
48
|
+
|
|
49
|
+
const openai = new OpenAI({
|
|
50
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const agent = new DocxAgent({
|
|
54
|
+
openai,
|
|
55
|
+
documentId: 'your-doc-id',
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Run with natural language
|
|
59
|
+
const result = await agent.run('Review this contract and flag any issues');
|
|
60
|
+
console.log(result.response);
|
|
61
|
+
console.log(result.toolCalls);
|
|
62
|
+
|
|
63
|
+
// Or stream for real-time UI
|
|
64
|
+
for await (const event of agent.runStream('Add comments to unclear sections')) {
|
|
65
|
+
if (event.type === 'content_delta') {
|
|
66
|
+
process.stdout.write(event.delta.text);
|
|
67
|
+
} else if (event.type === 'tool_start') {
|
|
68
|
+
console.log(`\nExecuting: ${event.tool.name}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Using with Kimi/Moonshot API
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import OpenAI from 'openai';
|
|
77
|
+
import { DocxAgent } from '@morphllm/subagents/docx';
|
|
78
|
+
|
|
79
|
+
const openai = new OpenAI({
|
|
80
|
+
apiKey: process.env.KIMI_API_KEY,
|
|
81
|
+
baseURL: 'https://api.moonshot.cn/v1',
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const agent = new DocxAgent({
|
|
85
|
+
openai,
|
|
86
|
+
model: 'moonshot-v1-32k',
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Module Imports
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// Import everything
|
|
94
|
+
import { DocxClient, DocxAgent, BaseClient } from '@morphllm/subagents';
|
|
95
|
+
|
|
96
|
+
// Import specific modules
|
|
97
|
+
import { DocxClient, DocxAgent } from '@morphllm/subagents/docx';
|
|
98
|
+
import { BaseClient, BaseAgent } from '@morphllm/subagents/core';
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Building Custom Subagents
|
|
102
|
+
|
|
103
|
+
Extend the base classes to create your own subagents:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { BaseClient, BaseAgent } from '@morphllm/subagents/core';
|
|
107
|
+
import type { Tool } from '@morphllm/subagents/core';
|
|
108
|
+
|
|
109
|
+
class MyClient extends BaseClient {
|
|
110
|
+
protected getDefaultApiUrl() {
|
|
111
|
+
return 'https://my-api.example.com';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async myMethod() {
|
|
115
|
+
return this.get('/my-endpoint');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
class MyAgent extends BaseAgent<MyClient> {
|
|
120
|
+
protected getDefaultModel() {
|
|
121
|
+
return 'gpt-4';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
protected getDefaultInstructions() {
|
|
125
|
+
return 'You are a helpful assistant.';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
protected getTools(): Tool[] {
|
|
129
|
+
return [/* your tools */];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
protected async executeTool(name: string, args: Record<string, unknown>) {
|
|
133
|
+
// Execute tool calls
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Environment Variables
|
|
139
|
+
|
|
140
|
+
| Variable | Description | Default |
|
|
141
|
+
|----------|-------------|---------|
|
|
142
|
+
| `DOCX_API_URL` | DOCX service URL | Railway deployment |
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
MIT
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { a as BaseClientOptions, B as BaseAgentOptions, T as Tool, C as ChatMessage, A as AgentRunResult, S as StreamEvent } from '../types-D9rS2MQq.mjs';
|
|
2
|
+
export { b as ToolCall } from '../types-D9rS2MQq.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* BaseClient - Abstract HTTP client for subagent APIs
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
declare abstract class BaseClient {
|
|
9
|
+
protected apiUrl: string;
|
|
10
|
+
protected timeout: number;
|
|
11
|
+
constructor(options?: BaseClientOptions);
|
|
12
|
+
/** Override in subclasses to provide default API URL */
|
|
13
|
+
protected abstract getDefaultApiUrl(): string;
|
|
14
|
+
/** Make a GET request */
|
|
15
|
+
protected get<T>(path: string): Promise<T>;
|
|
16
|
+
/** Make a POST request with JSON body */
|
|
17
|
+
protected post<T>(path: string, body?: unknown): Promise<T>;
|
|
18
|
+
/** Make a POST request with FormData */
|
|
19
|
+
protected postFormData<T>(path: string, formData: FormData): Promise<T>;
|
|
20
|
+
/** Make a DELETE request */
|
|
21
|
+
protected delete(path: string): Promise<void>;
|
|
22
|
+
/** Download a file as Blob */
|
|
23
|
+
protected downloadBlob(path: string): Promise<Blob>;
|
|
24
|
+
/** Health check */
|
|
25
|
+
health(): Promise<{
|
|
26
|
+
status: string;
|
|
27
|
+
service: string;
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* BaseAgent - Abstract base class for AI subagents
|
|
33
|
+
*
|
|
34
|
+
* Provides common functionality for tool-using agents with
|
|
35
|
+
* OpenAI-compatible API support.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
declare abstract class BaseAgent<TClient> {
|
|
39
|
+
protected openai: any;
|
|
40
|
+
protected model: string;
|
|
41
|
+
protected instructions: string;
|
|
42
|
+
protected client: TClient;
|
|
43
|
+
constructor(options: BaseAgentOptions & {
|
|
44
|
+
client: TClient;
|
|
45
|
+
});
|
|
46
|
+
/** Get the underlying client */
|
|
47
|
+
getClient(): TClient;
|
|
48
|
+
/** Override to provide default model */
|
|
49
|
+
protected abstract getDefaultModel(): string;
|
|
50
|
+
/** Override to provide default instructions */
|
|
51
|
+
protected abstract getDefaultInstructions(): string;
|
|
52
|
+
/** Override to provide tool definitions */
|
|
53
|
+
protected abstract getTools(): Tool[];
|
|
54
|
+
/** Override to execute a tool call */
|
|
55
|
+
protected abstract executeTool(name: string, args: Record<string, unknown>): Promise<string>;
|
|
56
|
+
/**
|
|
57
|
+
* Run the agent with a user message.
|
|
58
|
+
* Returns the final response and all tool calls made.
|
|
59
|
+
*/
|
|
60
|
+
run(userMessage: string, conversationHistory?: ChatMessage[]): Promise<AgentRunResult>;
|
|
61
|
+
/**
|
|
62
|
+
* Run the agent with streaming output.
|
|
63
|
+
* Yields events for real-time UI updates.
|
|
64
|
+
*/
|
|
65
|
+
runStream(userMessage: string, conversationHistory?: ChatMessage[]): AsyncGenerator<StreamEvent>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { AgentRunResult, BaseAgent, BaseAgentOptions, BaseClient, BaseClientOptions, ChatMessage, StreamEvent, Tool };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { a as BaseClientOptions, B as BaseAgentOptions, T as Tool, C as ChatMessage, A as AgentRunResult, S as StreamEvent } from '../types-D9rS2MQq.js';
|
|
2
|
+
export { b as ToolCall } from '../types-D9rS2MQq.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* BaseClient - Abstract HTTP client for subagent APIs
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
declare abstract class BaseClient {
|
|
9
|
+
protected apiUrl: string;
|
|
10
|
+
protected timeout: number;
|
|
11
|
+
constructor(options?: BaseClientOptions);
|
|
12
|
+
/** Override in subclasses to provide default API URL */
|
|
13
|
+
protected abstract getDefaultApiUrl(): string;
|
|
14
|
+
/** Make a GET request */
|
|
15
|
+
protected get<T>(path: string): Promise<T>;
|
|
16
|
+
/** Make a POST request with JSON body */
|
|
17
|
+
protected post<T>(path: string, body?: unknown): Promise<T>;
|
|
18
|
+
/** Make a POST request with FormData */
|
|
19
|
+
protected postFormData<T>(path: string, formData: FormData): Promise<T>;
|
|
20
|
+
/** Make a DELETE request */
|
|
21
|
+
protected delete(path: string): Promise<void>;
|
|
22
|
+
/** Download a file as Blob */
|
|
23
|
+
protected downloadBlob(path: string): Promise<Blob>;
|
|
24
|
+
/** Health check */
|
|
25
|
+
health(): Promise<{
|
|
26
|
+
status: string;
|
|
27
|
+
service: string;
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* BaseAgent - Abstract base class for AI subagents
|
|
33
|
+
*
|
|
34
|
+
* Provides common functionality for tool-using agents with
|
|
35
|
+
* OpenAI-compatible API support.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
declare abstract class BaseAgent<TClient> {
|
|
39
|
+
protected openai: any;
|
|
40
|
+
protected model: string;
|
|
41
|
+
protected instructions: string;
|
|
42
|
+
protected client: TClient;
|
|
43
|
+
constructor(options: BaseAgentOptions & {
|
|
44
|
+
client: TClient;
|
|
45
|
+
});
|
|
46
|
+
/** Get the underlying client */
|
|
47
|
+
getClient(): TClient;
|
|
48
|
+
/** Override to provide default model */
|
|
49
|
+
protected abstract getDefaultModel(): string;
|
|
50
|
+
/** Override to provide default instructions */
|
|
51
|
+
protected abstract getDefaultInstructions(): string;
|
|
52
|
+
/** Override to provide tool definitions */
|
|
53
|
+
protected abstract getTools(): Tool[];
|
|
54
|
+
/** Override to execute a tool call */
|
|
55
|
+
protected abstract executeTool(name: string, args: Record<string, unknown>): Promise<string>;
|
|
56
|
+
/**
|
|
57
|
+
* Run the agent with a user message.
|
|
58
|
+
* Returns the final response and all tool calls made.
|
|
59
|
+
*/
|
|
60
|
+
run(userMessage: string, conversationHistory?: ChatMessage[]): Promise<AgentRunResult>;
|
|
61
|
+
/**
|
|
62
|
+
* Run the agent with streaming output.
|
|
63
|
+
* Yields events for real-time UI updates.
|
|
64
|
+
*/
|
|
65
|
+
runStream(userMessage: string, conversationHistory?: ChatMessage[]): AsyncGenerator<StreamEvent>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { AgentRunResult, BaseAgent, BaseAgentOptions, BaseClient, BaseClientOptions, ChatMessage, StreamEvent, Tool };
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/core/base-client.ts
|
|
4
|
+
var BaseClient = class {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.apiUrl = options.apiUrl || this.getDefaultApiUrl();
|
|
7
|
+
this.timeout = options.timeout || 6e4;
|
|
8
|
+
}
|
|
9
|
+
/** Make a GET request */
|
|
10
|
+
async get(path) {
|
|
11
|
+
const response = await fetch(`${this.apiUrl}${path}`, {
|
|
12
|
+
method: "GET",
|
|
13
|
+
signal: AbortSignal.timeout(this.timeout)
|
|
14
|
+
});
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
throw new Error(`GET ${path} failed: ${response.statusText}`);
|
|
17
|
+
}
|
|
18
|
+
return response.json();
|
|
19
|
+
}
|
|
20
|
+
/** Make a POST request with JSON body */
|
|
21
|
+
async post(path, body) {
|
|
22
|
+
const response = await fetch(`${this.apiUrl}${path}`, {
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: body ? { "Content-Type": "application/json" } : void 0,
|
|
25
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
26
|
+
signal: AbortSignal.timeout(this.timeout)
|
|
27
|
+
});
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
throw new Error(`POST ${path} failed: ${response.statusText}`);
|
|
30
|
+
}
|
|
31
|
+
return response.json();
|
|
32
|
+
}
|
|
33
|
+
/** Make a POST request with FormData */
|
|
34
|
+
async postFormData(path, formData) {
|
|
35
|
+
const response = await fetch(`${this.apiUrl}${path}`, {
|
|
36
|
+
method: "POST",
|
|
37
|
+
body: formData,
|
|
38
|
+
signal: AbortSignal.timeout(this.timeout)
|
|
39
|
+
});
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
throw new Error(`POST ${path} failed: ${response.statusText}`);
|
|
42
|
+
}
|
|
43
|
+
return response.json();
|
|
44
|
+
}
|
|
45
|
+
/** Make a DELETE request */
|
|
46
|
+
async delete(path) {
|
|
47
|
+
const response = await fetch(`${this.apiUrl}${path}`, {
|
|
48
|
+
method: "DELETE",
|
|
49
|
+
signal: AbortSignal.timeout(this.timeout)
|
|
50
|
+
});
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
throw new Error(`DELETE ${path} failed: ${response.statusText}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Download a file as Blob */
|
|
56
|
+
async downloadBlob(path) {
|
|
57
|
+
const response = await fetch(`${this.apiUrl}${path}`, {
|
|
58
|
+
signal: AbortSignal.timeout(this.timeout)
|
|
59
|
+
});
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
throw new Error(`Download ${path} failed: ${response.statusText}`);
|
|
62
|
+
}
|
|
63
|
+
return response.blob();
|
|
64
|
+
}
|
|
65
|
+
/** Health check */
|
|
66
|
+
async health() {
|
|
67
|
+
return this.get("/health");
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// src/core/base-agent.ts
|
|
72
|
+
var BaseAgent = class {
|
|
73
|
+
constructor(options) {
|
|
74
|
+
this.openai = options.openai;
|
|
75
|
+
this.model = options.model || this.getDefaultModel();
|
|
76
|
+
this.instructions = options.instructions || this.getDefaultInstructions();
|
|
77
|
+
this.client = options.client;
|
|
78
|
+
}
|
|
79
|
+
/** Get the underlying client */
|
|
80
|
+
getClient() {
|
|
81
|
+
return this.client;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Run the agent with a user message.
|
|
85
|
+
* Returns the final response and all tool calls made.
|
|
86
|
+
*/
|
|
87
|
+
async run(userMessage, conversationHistory = []) {
|
|
88
|
+
if (!this.openai) {
|
|
89
|
+
throw new Error("OpenAI client is required for agent.run(). Pass it in the constructor options.");
|
|
90
|
+
}
|
|
91
|
+
const messages = [
|
|
92
|
+
{ role: "system", content: this.instructions },
|
|
93
|
+
...conversationHistory,
|
|
94
|
+
{ role: "user", content: userMessage }
|
|
95
|
+
];
|
|
96
|
+
const toolCalls = [];
|
|
97
|
+
let response = "";
|
|
98
|
+
const tools = this.getTools();
|
|
99
|
+
while (true) {
|
|
100
|
+
const completion = await this.openai.chat.completions.create({
|
|
101
|
+
model: this.model,
|
|
102
|
+
messages,
|
|
103
|
+
tools,
|
|
104
|
+
tool_choice: "auto"
|
|
105
|
+
});
|
|
106
|
+
const assistantMessage = completion.choices[0].message;
|
|
107
|
+
if (assistantMessage.tool_calls && assistantMessage.tool_calls.length > 0) {
|
|
108
|
+
messages.push({
|
|
109
|
+
role: "assistant",
|
|
110
|
+
content: assistantMessage.content || ""
|
|
111
|
+
});
|
|
112
|
+
for (const tc of assistantMessage.tool_calls) {
|
|
113
|
+
const fn = tc.function;
|
|
114
|
+
const args = JSON.parse(fn.arguments);
|
|
115
|
+
const toolCall = {
|
|
116
|
+
id: tc.id,
|
|
117
|
+
name: fn.name,
|
|
118
|
+
arguments: args,
|
|
119
|
+
status: "running"
|
|
120
|
+
};
|
|
121
|
+
toolCalls.push(toolCall);
|
|
122
|
+
const result = await this.executeTool(fn.name, args);
|
|
123
|
+
if (result.startsWith("Error:")) {
|
|
124
|
+
toolCall.error = result;
|
|
125
|
+
toolCall.status = "error";
|
|
126
|
+
} else {
|
|
127
|
+
toolCall.result = result;
|
|
128
|
+
toolCall.status = "completed";
|
|
129
|
+
}
|
|
130
|
+
messages.push({
|
|
131
|
+
role: "tool",
|
|
132
|
+
content: result,
|
|
133
|
+
tool_call_id: tc.id
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
response = assistantMessage.content || "";
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return { response, toolCalls };
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Run the agent with streaming output.
|
|
145
|
+
* Yields events for real-time UI updates.
|
|
146
|
+
*/
|
|
147
|
+
async *runStream(userMessage, conversationHistory = []) {
|
|
148
|
+
if (!this.openai) {
|
|
149
|
+
throw new Error("OpenAI client is required for agent.runStream(). Pass it in the constructor options.");
|
|
150
|
+
}
|
|
151
|
+
const messages = [
|
|
152
|
+
{ role: "system", content: this.instructions },
|
|
153
|
+
...conversationHistory,
|
|
154
|
+
{ role: "user", content: userMessage }
|
|
155
|
+
];
|
|
156
|
+
const tools = this.getTools();
|
|
157
|
+
while (true) {
|
|
158
|
+
yield { type: "message_start", message: { role: "assistant" } };
|
|
159
|
+
const stream = await this.openai.chat.completions.create({
|
|
160
|
+
model: this.model,
|
|
161
|
+
messages,
|
|
162
|
+
tools,
|
|
163
|
+
tool_choice: "auto",
|
|
164
|
+
stream: true
|
|
165
|
+
});
|
|
166
|
+
let contentBuffer = "";
|
|
167
|
+
const toolCallBuffers = /* @__PURE__ */ new Map();
|
|
168
|
+
for await (const chunk of stream) {
|
|
169
|
+
const delta = chunk.choices[0]?.delta;
|
|
170
|
+
if (delta?.content) {
|
|
171
|
+
contentBuffer += delta.content;
|
|
172
|
+
yield { type: "content_delta", delta: { text: delta.content } };
|
|
173
|
+
}
|
|
174
|
+
if (delta?.tool_calls) {
|
|
175
|
+
for (const tc of delta.tool_calls) {
|
|
176
|
+
const idx = tc.index;
|
|
177
|
+
if (!toolCallBuffers.has(idx)) {
|
|
178
|
+
toolCallBuffers.set(idx, { id: "", name: "", arguments: "" });
|
|
179
|
+
}
|
|
180
|
+
const buffer = toolCallBuffers.get(idx);
|
|
181
|
+
if (tc.id) buffer.id = tc.id;
|
|
182
|
+
if (tc.function?.name) buffer.name = tc.function.name;
|
|
183
|
+
if (tc.function?.arguments) buffer.arguments += tc.function.arguments;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (toolCallBuffers.size > 0) {
|
|
188
|
+
messages.push({
|
|
189
|
+
role: "assistant",
|
|
190
|
+
content: contentBuffer
|
|
191
|
+
});
|
|
192
|
+
for (const [_, buffer] of toolCallBuffers) {
|
|
193
|
+
const args = JSON.parse(buffer.arguments);
|
|
194
|
+
yield {
|
|
195
|
+
type: "tool_start",
|
|
196
|
+
tool: { id: buffer.id, name: buffer.name, arguments: args }
|
|
197
|
+
};
|
|
198
|
+
const result = await this.executeTool(buffer.name, args);
|
|
199
|
+
if (result.startsWith("Error:")) {
|
|
200
|
+
yield { type: "tool_end", tool: { id: buffer.id, error: result } };
|
|
201
|
+
} else {
|
|
202
|
+
yield { type: "tool_end", tool: { id: buffer.id, result } };
|
|
203
|
+
}
|
|
204
|
+
messages.push({
|
|
205
|
+
role: "tool",
|
|
206
|
+
content: result,
|
|
207
|
+
tool_call_id: buffer.id
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
yield { type: "message_end" };
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
exports.BaseAgent = BaseAgent;
|
|
219
|
+
exports.BaseClient = BaseClient;
|
|
220
|
+
//# sourceMappingURL=index.js.map
|
|
221
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/base-client.ts","../../src/core/base-agent.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * BaseClient - Abstract HTTP client for subagent APIs\n */\n\nimport type { BaseClientOptions } from './types';\n\nexport abstract class BaseClient {\n protected apiUrl: string;\n protected timeout: number;\n\n constructor(options: BaseClientOptions = {}) {\n this.apiUrl = options.apiUrl || this.getDefaultApiUrl();\n this.timeout = options.timeout || 60000;\n }\n\n /** Override in subclasses to provide default API URL */\n protected abstract getDefaultApiUrl(): string;\n\n /** Make a GET request */\n protected async get<T>(path: string): Promise<T> {\n const response = await fetch(`${this.apiUrl}${path}`, {\n method: 'GET',\n signal: AbortSignal.timeout(this.timeout),\n });\n\n if (!response.ok) {\n throw new Error(`GET ${path} failed: ${response.statusText}`);\n }\n\n return response.json();\n }\n\n /** Make a POST request with JSON body */\n protected async post<T>(path: string, body?: unknown): Promise<T> {\n const response = await fetch(`${this.apiUrl}${path}`, {\n method: 'POST',\n headers: body ? { 'Content-Type': 'application/json' } : undefined,\n body: body ? JSON.stringify(body) : undefined,\n signal: AbortSignal.timeout(this.timeout),\n });\n\n if (!response.ok) {\n throw new Error(`POST ${path} failed: ${response.statusText}`);\n }\n\n return response.json();\n }\n\n /** Make a POST request with FormData */\n protected async postFormData<T>(path: string, formData: FormData): Promise<T> {\n const response = await fetch(`${this.apiUrl}${path}`, {\n method: 'POST',\n body: formData,\n signal: AbortSignal.timeout(this.timeout),\n });\n\n if (!response.ok) {\n throw new Error(`POST ${path} failed: ${response.statusText}`);\n }\n\n return response.json();\n }\n\n /** Make a DELETE request */\n protected async delete(path: string): Promise<void> {\n const response = await fetch(`${this.apiUrl}${path}`, {\n method: 'DELETE',\n signal: AbortSignal.timeout(this.timeout),\n });\n\n if (!response.ok) {\n throw new Error(`DELETE ${path} failed: ${response.statusText}`);\n }\n }\n\n /** Download a file as Blob */\n protected async downloadBlob(path: string): Promise<Blob> {\n const response = await fetch(`${this.apiUrl}${path}`, {\n signal: AbortSignal.timeout(this.timeout),\n });\n\n if (!response.ok) {\n throw new Error(`Download ${path} failed: ${response.statusText}`);\n }\n\n return response.blob();\n }\n\n /** Health check */\n async health(): Promise<{ status: string; service: string }> {\n return this.get('/health');\n }\n}\n","/**\n * BaseAgent - Abstract base class for AI subagents\n *\n * Provides common functionality for tool-using agents with\n * OpenAI-compatible API support.\n */\n\nimport type {\n Tool,\n ChatMessage,\n ToolCall,\n StreamEvent,\n AgentRunResult,\n BaseAgentOptions,\n} from './types';\n\nexport abstract class BaseAgent<TClient> {\n protected openai: any;\n protected model: string;\n protected instructions: string;\n protected client: TClient;\n\n constructor(options: BaseAgentOptions & { client: TClient }) {\n this.openai = options.openai;\n this.model = options.model || this.getDefaultModel();\n this.instructions = options.instructions || this.getDefaultInstructions();\n this.client = options.client;\n }\n\n /** Get the underlying client */\n getClient(): TClient {\n return this.client;\n }\n\n /** Override to provide default model */\n protected abstract getDefaultModel(): string;\n\n /** Override to provide default instructions */\n protected abstract getDefaultInstructions(): string;\n\n /** Override to provide tool definitions */\n protected abstract getTools(): Tool[];\n\n /** Override to execute a tool call */\n protected abstract executeTool(name: string, args: Record<string, unknown>): Promise<string>;\n\n /**\n * Run the agent with a user message.\n * Returns the final response and all tool calls made.\n */\n async run(\n userMessage: string,\n conversationHistory: ChatMessage[] = []\n ): Promise<AgentRunResult> {\n if (!this.openai) {\n throw new Error('OpenAI client is required for agent.run(). Pass it in the constructor options.');\n }\n\n const messages: ChatMessage[] = [\n { role: 'system', content: this.instructions },\n ...conversationHistory,\n { role: 'user', content: userMessage },\n ];\n\n const toolCalls: ToolCall[] = [];\n let response = '';\n const tools = this.getTools();\n\n // Agent loop - keep running until no more tool calls\n while (true) {\n const completion = await this.openai.chat.completions.create({\n model: this.model,\n messages: messages as any,\n tools: tools as any,\n tool_choice: 'auto',\n });\n\n const assistantMessage = completion.choices[0].message;\n\n // Check if there are tool calls\n if (assistantMessage.tool_calls && assistantMessage.tool_calls.length > 0) {\n // Add assistant message with tool calls\n messages.push({\n role: 'assistant',\n content: assistantMessage.content || '',\n });\n\n // Execute each tool call\n for (const tc of assistantMessage.tool_calls) {\n const fn = (tc as any).function;\n const args = JSON.parse(fn.arguments);\n\n const toolCall: ToolCall = {\n id: tc.id,\n name: fn.name,\n arguments: args,\n status: 'running',\n };\n toolCalls.push(toolCall);\n\n // Execute the tool\n const result = await this.executeTool(fn.name, args);\n\n // Update tool call status\n if (result.startsWith('Error:')) {\n toolCall.error = result;\n toolCall.status = 'error';\n } else {\n toolCall.result = result;\n toolCall.status = 'completed';\n }\n\n // Add tool result to messages\n messages.push({\n role: 'tool',\n content: result,\n tool_call_id: tc.id,\n });\n }\n } else {\n // No more tool calls - return the final response\n response = assistantMessage.content || '';\n break;\n }\n }\n\n return { response, toolCalls };\n }\n\n /**\n * Run the agent with streaming output.\n * Yields events for real-time UI updates.\n */\n async *runStream(\n userMessage: string,\n conversationHistory: ChatMessage[] = []\n ): AsyncGenerator<StreamEvent> {\n if (!this.openai) {\n throw new Error('OpenAI client is required for agent.runStream(). Pass it in the constructor options.');\n }\n\n const messages: ChatMessage[] = [\n { role: 'system', content: this.instructions },\n ...conversationHistory,\n { role: 'user', content: userMessage },\n ];\n\n const tools = this.getTools();\n\n // Agent loop\n while (true) {\n yield { type: 'message_start', message: { role: 'assistant' } };\n\n const stream = await this.openai.chat.completions.create({\n model: this.model,\n messages: messages as any,\n tools: tools as any,\n tool_choice: 'auto',\n stream: true,\n });\n\n let contentBuffer = '';\n const toolCallBuffers: Map<number, { id: string; name: string; arguments: string }> =\n new Map();\n\n for await (const chunk of stream) {\n const delta = chunk.choices[0]?.delta;\n\n // Handle content\n if (delta?.content) {\n contentBuffer += delta.content;\n yield { type: 'content_delta', delta: { text: delta.content } };\n }\n\n // Handle tool calls\n if (delta?.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index;\n if (!toolCallBuffers.has(idx)) {\n toolCallBuffers.set(idx, { id: '', name: '', arguments: '' });\n }\n\n const buffer = toolCallBuffers.get(idx)!;\n if (tc.id) buffer.id = tc.id;\n if (tc.function?.name) buffer.name = tc.function.name;\n if (tc.function?.arguments) buffer.arguments += tc.function.arguments;\n }\n }\n }\n\n // Process tool calls if any\n if (toolCallBuffers.size > 0) {\n // Add assistant message to history\n messages.push({\n role: 'assistant',\n content: contentBuffer,\n });\n\n for (const [_, buffer] of toolCallBuffers) {\n const args = JSON.parse(buffer.arguments);\n\n yield {\n type: 'tool_start',\n tool: { id: buffer.id, name: buffer.name, arguments: args },\n };\n\n // Execute the tool\n const result = await this.executeTool(buffer.name, args);\n\n if (result.startsWith('Error:')) {\n yield { type: 'tool_end', tool: { id: buffer.id, error: result } };\n } else {\n yield { type: 'tool_end', tool: { id: buffer.id, result } };\n }\n\n // Add tool result to messages\n messages.push({\n role: 'tool',\n content: result,\n tool_call_id: buffer.id,\n });\n }\n } else {\n // No more tool calls - done\n yield { type: 'message_end' };\n break;\n }\n }\n }\n}\n"]}
|