@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/pdf/index.ts"],"names":[],"mappings":";AAqBO,IAAM,SAAA,GAAY;AAClB,IAAM,QAAA,GAAW","file":"index.mjs","sourcesContent":["/**\n * @morphllm/subagents/pdf\n *\n * PDF form filling subagent (coming soon).\n *\n * This module will provide:\n * - PdfClient: HTTP client for PDF form API\n * - PdfAgent: AI agent for filling PDF forms\n */\n\nexport type {\n PdfDocumentInfo,\n FormField,\n ReadFieldsResponse,\n FillOperation,\n FillResponse,\n PdfClientOptions,\n PdfAgentOptions,\n} from './types';\n\n// Placeholder exports - will be implemented when PDF service is ready\nexport const PdfClient = null;\nexport const PdfAgent = null;\n"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { B as BaseAgentOptions, a as BaseClientOptions } from './types-D9rS2MQq.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Types for PDF subagent (placeholder for future implementation)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** PDF document info */
|
|
8
|
+
interface PdfDocumentInfo {
|
|
9
|
+
doc_id: string;
|
|
10
|
+
page_count: number;
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
/** Form field information */
|
|
14
|
+
interface FormField {
|
|
15
|
+
name: string;
|
|
16
|
+
type: 'text' | 'checkbox' | 'radio' | 'dropdown' | 'signature';
|
|
17
|
+
value?: string;
|
|
18
|
+
options?: string[];
|
|
19
|
+
required?: boolean;
|
|
20
|
+
page: number;
|
|
21
|
+
}
|
|
22
|
+
/** Response from reading PDF form fields */
|
|
23
|
+
interface ReadFieldsResponse {
|
|
24
|
+
fields: FormField[];
|
|
25
|
+
page_count: number;
|
|
26
|
+
}
|
|
27
|
+
/** Fill operation for a form field */
|
|
28
|
+
interface FillOperation {
|
|
29
|
+
field_name: string;
|
|
30
|
+
value: string;
|
|
31
|
+
}
|
|
32
|
+
/** Response from fill operations */
|
|
33
|
+
interface FillResponse {
|
|
34
|
+
results: string[];
|
|
35
|
+
doc_id: string;
|
|
36
|
+
}
|
|
37
|
+
/** PDF client options */
|
|
38
|
+
interface PdfClientOptions extends BaseClientOptions {
|
|
39
|
+
/** API URL for PDF service */
|
|
40
|
+
apiUrl?: string;
|
|
41
|
+
}
|
|
42
|
+
/** PDF agent options */
|
|
43
|
+
interface PdfAgentOptions extends BaseAgentOptions {
|
|
44
|
+
/** Current document ID */
|
|
45
|
+
documentId?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type { FillOperation as F, PdfAgentOptions as P, ReadFieldsResponse as R, FillResponse as a, FormField as b, PdfClientOptions as c, PdfDocumentInfo as d };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types shared across all subagents
|
|
3
|
+
*/
|
|
4
|
+
/** Tool definition for OpenAI-compatible APIs */
|
|
5
|
+
interface Tool {
|
|
6
|
+
type: 'function';
|
|
7
|
+
function: {
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
parameters: Record<string, unknown>;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/** Chat message for conversation history */
|
|
14
|
+
interface ChatMessage {
|
|
15
|
+
role: 'system' | 'user' | 'assistant' | 'tool';
|
|
16
|
+
content: string;
|
|
17
|
+
name?: string;
|
|
18
|
+
tool_call_id?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Tool call tracking */
|
|
21
|
+
interface ToolCall {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
arguments: Record<string, unknown>;
|
|
25
|
+
result?: string;
|
|
26
|
+
error?: string;
|
|
27
|
+
status: 'pending' | 'running' | 'completed' | 'error';
|
|
28
|
+
}
|
|
29
|
+
/** Stream events for real-time UI updates */
|
|
30
|
+
type StreamEvent = {
|
|
31
|
+
type: 'message_start';
|
|
32
|
+
message: {
|
|
33
|
+
role: 'assistant';
|
|
34
|
+
};
|
|
35
|
+
} | {
|
|
36
|
+
type: 'content_delta';
|
|
37
|
+
delta: {
|
|
38
|
+
text: string;
|
|
39
|
+
};
|
|
40
|
+
} | {
|
|
41
|
+
type: 'tool_start';
|
|
42
|
+
tool: {
|
|
43
|
+
id: string;
|
|
44
|
+
name: string;
|
|
45
|
+
arguments: Record<string, unknown>;
|
|
46
|
+
};
|
|
47
|
+
} | {
|
|
48
|
+
type: 'tool_end';
|
|
49
|
+
tool: {
|
|
50
|
+
id: string;
|
|
51
|
+
result?: string;
|
|
52
|
+
error?: string;
|
|
53
|
+
};
|
|
54
|
+
} | {
|
|
55
|
+
type: 'message_end';
|
|
56
|
+
} | {
|
|
57
|
+
type: 'error';
|
|
58
|
+
error: string;
|
|
59
|
+
};
|
|
60
|
+
/** Agent run result */
|
|
61
|
+
interface AgentRunResult {
|
|
62
|
+
response: string;
|
|
63
|
+
toolCalls: ToolCall[];
|
|
64
|
+
}
|
|
65
|
+
/** Base client options */
|
|
66
|
+
interface BaseClientOptions {
|
|
67
|
+
/** API base URL */
|
|
68
|
+
apiUrl?: string;
|
|
69
|
+
/** Request timeout in milliseconds */
|
|
70
|
+
timeout?: number;
|
|
71
|
+
}
|
|
72
|
+
/** Base agent options */
|
|
73
|
+
interface BaseAgentOptions extends BaseClientOptions {
|
|
74
|
+
/** OpenAI-compatible client instance */
|
|
75
|
+
openai?: any;
|
|
76
|
+
/** Model to use */
|
|
77
|
+
model?: string;
|
|
78
|
+
/** System instructions */
|
|
79
|
+
instructions?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export type { AgentRunResult as A, BaseAgentOptions as B, ChatMessage as C, StreamEvent as S, Tool as T, BaseClientOptions as a, ToolCall as b };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types shared across all subagents
|
|
3
|
+
*/
|
|
4
|
+
/** Tool definition for OpenAI-compatible APIs */
|
|
5
|
+
interface Tool {
|
|
6
|
+
type: 'function';
|
|
7
|
+
function: {
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
parameters: Record<string, unknown>;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/** Chat message for conversation history */
|
|
14
|
+
interface ChatMessage {
|
|
15
|
+
role: 'system' | 'user' | 'assistant' | 'tool';
|
|
16
|
+
content: string;
|
|
17
|
+
name?: string;
|
|
18
|
+
tool_call_id?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Tool call tracking */
|
|
21
|
+
interface ToolCall {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
arguments: Record<string, unknown>;
|
|
25
|
+
result?: string;
|
|
26
|
+
error?: string;
|
|
27
|
+
status: 'pending' | 'running' | 'completed' | 'error';
|
|
28
|
+
}
|
|
29
|
+
/** Stream events for real-time UI updates */
|
|
30
|
+
type StreamEvent = {
|
|
31
|
+
type: 'message_start';
|
|
32
|
+
message: {
|
|
33
|
+
role: 'assistant';
|
|
34
|
+
};
|
|
35
|
+
} | {
|
|
36
|
+
type: 'content_delta';
|
|
37
|
+
delta: {
|
|
38
|
+
text: string;
|
|
39
|
+
};
|
|
40
|
+
} | {
|
|
41
|
+
type: 'tool_start';
|
|
42
|
+
tool: {
|
|
43
|
+
id: string;
|
|
44
|
+
name: string;
|
|
45
|
+
arguments: Record<string, unknown>;
|
|
46
|
+
};
|
|
47
|
+
} | {
|
|
48
|
+
type: 'tool_end';
|
|
49
|
+
tool: {
|
|
50
|
+
id: string;
|
|
51
|
+
result?: string;
|
|
52
|
+
error?: string;
|
|
53
|
+
};
|
|
54
|
+
} | {
|
|
55
|
+
type: 'message_end';
|
|
56
|
+
} | {
|
|
57
|
+
type: 'error';
|
|
58
|
+
error: string;
|
|
59
|
+
};
|
|
60
|
+
/** Agent run result */
|
|
61
|
+
interface AgentRunResult {
|
|
62
|
+
response: string;
|
|
63
|
+
toolCalls: ToolCall[];
|
|
64
|
+
}
|
|
65
|
+
/** Base client options */
|
|
66
|
+
interface BaseClientOptions {
|
|
67
|
+
/** API base URL */
|
|
68
|
+
apiUrl?: string;
|
|
69
|
+
/** Request timeout in milliseconds */
|
|
70
|
+
timeout?: number;
|
|
71
|
+
}
|
|
72
|
+
/** Base agent options */
|
|
73
|
+
interface BaseAgentOptions extends BaseClientOptions {
|
|
74
|
+
/** OpenAI-compatible client instance */
|
|
75
|
+
openai?: any;
|
|
76
|
+
/** Model to use */
|
|
77
|
+
model?: string;
|
|
78
|
+
/** System instructions */
|
|
79
|
+
instructions?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export type { AgentRunResult as A, BaseAgentOptions as B, ChatMessage as C, StreamEvent as S, Tool as T, BaseClientOptions as a, ToolCall as b };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { B as BaseAgentOptions, a as BaseClientOptions } from './types-D9rS2MQq.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Types for PDF subagent (placeholder for future implementation)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** PDF document info */
|
|
8
|
+
interface PdfDocumentInfo {
|
|
9
|
+
doc_id: string;
|
|
10
|
+
page_count: number;
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
/** Form field information */
|
|
14
|
+
interface FormField {
|
|
15
|
+
name: string;
|
|
16
|
+
type: 'text' | 'checkbox' | 'radio' | 'dropdown' | 'signature';
|
|
17
|
+
value?: string;
|
|
18
|
+
options?: string[];
|
|
19
|
+
required?: boolean;
|
|
20
|
+
page: number;
|
|
21
|
+
}
|
|
22
|
+
/** Response from reading PDF form fields */
|
|
23
|
+
interface ReadFieldsResponse {
|
|
24
|
+
fields: FormField[];
|
|
25
|
+
page_count: number;
|
|
26
|
+
}
|
|
27
|
+
/** Fill operation for a form field */
|
|
28
|
+
interface FillOperation {
|
|
29
|
+
field_name: string;
|
|
30
|
+
value: string;
|
|
31
|
+
}
|
|
32
|
+
/** Response from fill operations */
|
|
33
|
+
interface FillResponse {
|
|
34
|
+
results: string[];
|
|
35
|
+
doc_id: string;
|
|
36
|
+
}
|
|
37
|
+
/** PDF client options */
|
|
38
|
+
interface PdfClientOptions extends BaseClientOptions {
|
|
39
|
+
/** API URL for PDF service */
|
|
40
|
+
apiUrl?: string;
|
|
41
|
+
}
|
|
42
|
+
/** PDF agent options */
|
|
43
|
+
interface PdfAgentOptions extends BaseAgentOptions {
|
|
44
|
+
/** Current document ID */
|
|
45
|
+
documentId?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type { FillOperation as F, PdfAgentOptions as P, ReadFieldsResponse as R, FillResponse as a, FormField as b, PdfClientOptions as c, PdfDocumentInfo as d };
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@morphllm/subagents",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Modular AI subagents for document processing - DOCX editing, PDF filling, and more",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./core": {
|
|
15
|
+
"types": "./dist/core/index.d.ts",
|
|
16
|
+
"import": "./dist/core/index.mjs",
|
|
17
|
+
"require": "./dist/core/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./docx": {
|
|
20
|
+
"types": "./dist/docx/index.d.ts",
|
|
21
|
+
"import": "./dist/docx/index.mjs",
|
|
22
|
+
"require": "./dist/docx/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./pdf": {
|
|
25
|
+
"types": "./dist/pdf/index.d.ts",
|
|
26
|
+
"import": "./dist/pdf/index.mjs",
|
|
27
|
+
"require": "./dist/pdf/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"src"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsup",
|
|
36
|
+
"dev": "tsup --watch",
|
|
37
|
+
"prepublishOnly": "npm run build",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"test:watch": "vitest"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"ai",
|
|
43
|
+
"agent",
|
|
44
|
+
"subagent",
|
|
45
|
+
"docx",
|
|
46
|
+
"pdf",
|
|
47
|
+
"document",
|
|
48
|
+
"editing",
|
|
49
|
+
"openai",
|
|
50
|
+
"morphllm"
|
|
51
|
+
],
|
|
52
|
+
"author": "MorphLLM",
|
|
53
|
+
"license": "MIT",
|
|
54
|
+
"repository": {
|
|
55
|
+
"type": "git",
|
|
56
|
+
"url": "https://github.com/morphllm/subagents"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"tsup": "^8.0.0",
|
|
60
|
+
"typescript": "^5.0.0",
|
|
61
|
+
"vitest": "^2.0.0"
|
|
62
|
+
},
|
|
63
|
+
"peerDependencies": {
|
|
64
|
+
"openai": ">=4.0.0"
|
|
65
|
+
},
|
|
66
|
+
"peerDependenciesMeta": {
|
|
67
|
+
"openai": {
|
|
68
|
+
"optional": true
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BaseAgent - Abstract base class for AI subagents
|
|
3
|
+
*
|
|
4
|
+
* Provides common functionality for tool-using agents with
|
|
5
|
+
* OpenAI-compatible API support.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
Tool,
|
|
10
|
+
ChatMessage,
|
|
11
|
+
ToolCall,
|
|
12
|
+
StreamEvent,
|
|
13
|
+
AgentRunResult,
|
|
14
|
+
BaseAgentOptions,
|
|
15
|
+
} from './types';
|
|
16
|
+
|
|
17
|
+
export abstract class BaseAgent<TClient> {
|
|
18
|
+
protected openai: any;
|
|
19
|
+
protected model: string;
|
|
20
|
+
protected instructions: string;
|
|
21
|
+
protected client: TClient;
|
|
22
|
+
|
|
23
|
+
constructor(options: BaseAgentOptions & { client: TClient }) {
|
|
24
|
+
this.openai = options.openai;
|
|
25
|
+
this.model = options.model || this.getDefaultModel();
|
|
26
|
+
this.instructions = options.instructions || this.getDefaultInstructions();
|
|
27
|
+
this.client = options.client;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Get the underlying client */
|
|
31
|
+
getClient(): TClient {
|
|
32
|
+
return this.client;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Override to provide default model */
|
|
36
|
+
protected abstract getDefaultModel(): string;
|
|
37
|
+
|
|
38
|
+
/** Override to provide default instructions */
|
|
39
|
+
protected abstract getDefaultInstructions(): string;
|
|
40
|
+
|
|
41
|
+
/** Override to provide tool definitions */
|
|
42
|
+
protected abstract getTools(): Tool[];
|
|
43
|
+
|
|
44
|
+
/** Override to execute a tool call */
|
|
45
|
+
protected abstract executeTool(name: string, args: Record<string, unknown>): Promise<string>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Run the agent with a user message.
|
|
49
|
+
* Returns the final response and all tool calls made.
|
|
50
|
+
*/
|
|
51
|
+
async run(
|
|
52
|
+
userMessage: string,
|
|
53
|
+
conversationHistory: ChatMessage[] = []
|
|
54
|
+
): Promise<AgentRunResult> {
|
|
55
|
+
if (!this.openai) {
|
|
56
|
+
throw new Error('OpenAI client is required for agent.run(). Pass it in the constructor options.');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const messages: ChatMessage[] = [
|
|
60
|
+
{ role: 'system', content: this.instructions },
|
|
61
|
+
...conversationHistory,
|
|
62
|
+
{ role: 'user', content: userMessage },
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
const toolCalls: ToolCall[] = [];
|
|
66
|
+
let response = '';
|
|
67
|
+
const tools = this.getTools();
|
|
68
|
+
|
|
69
|
+
// Agent loop - keep running until no more tool calls
|
|
70
|
+
while (true) {
|
|
71
|
+
const completion = await this.openai.chat.completions.create({
|
|
72
|
+
model: this.model,
|
|
73
|
+
messages: messages as any,
|
|
74
|
+
tools: tools as any,
|
|
75
|
+
tool_choice: 'auto',
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const assistantMessage = completion.choices[0].message;
|
|
79
|
+
|
|
80
|
+
// Check if there are tool calls
|
|
81
|
+
if (assistantMessage.tool_calls && assistantMessage.tool_calls.length > 0) {
|
|
82
|
+
// Add assistant message with tool calls
|
|
83
|
+
messages.push({
|
|
84
|
+
role: 'assistant',
|
|
85
|
+
content: assistantMessage.content || '',
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Execute each tool call
|
|
89
|
+
for (const tc of assistantMessage.tool_calls) {
|
|
90
|
+
const fn = (tc as any).function;
|
|
91
|
+
const args = JSON.parse(fn.arguments);
|
|
92
|
+
|
|
93
|
+
const toolCall: ToolCall = {
|
|
94
|
+
id: tc.id,
|
|
95
|
+
name: fn.name,
|
|
96
|
+
arguments: args,
|
|
97
|
+
status: 'running',
|
|
98
|
+
};
|
|
99
|
+
toolCalls.push(toolCall);
|
|
100
|
+
|
|
101
|
+
// Execute the tool
|
|
102
|
+
const result = await this.executeTool(fn.name, args);
|
|
103
|
+
|
|
104
|
+
// Update tool call status
|
|
105
|
+
if (result.startsWith('Error:')) {
|
|
106
|
+
toolCall.error = result;
|
|
107
|
+
toolCall.status = 'error';
|
|
108
|
+
} else {
|
|
109
|
+
toolCall.result = result;
|
|
110
|
+
toolCall.status = 'completed';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Add tool result to messages
|
|
114
|
+
messages.push({
|
|
115
|
+
role: 'tool',
|
|
116
|
+
content: result,
|
|
117
|
+
tool_call_id: tc.id,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
// No more tool calls - return the final response
|
|
122
|
+
response = assistantMessage.content || '';
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return { response, toolCalls };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Run the agent with streaming output.
|
|
132
|
+
* Yields events for real-time UI updates.
|
|
133
|
+
*/
|
|
134
|
+
async *runStream(
|
|
135
|
+
userMessage: string,
|
|
136
|
+
conversationHistory: ChatMessage[] = []
|
|
137
|
+
): AsyncGenerator<StreamEvent> {
|
|
138
|
+
if (!this.openai) {
|
|
139
|
+
throw new Error('OpenAI client is required for agent.runStream(). Pass it in the constructor options.');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const messages: ChatMessage[] = [
|
|
143
|
+
{ role: 'system', content: this.instructions },
|
|
144
|
+
...conversationHistory,
|
|
145
|
+
{ role: 'user', content: userMessage },
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
const tools = this.getTools();
|
|
149
|
+
|
|
150
|
+
// Agent loop
|
|
151
|
+
while (true) {
|
|
152
|
+
yield { type: 'message_start', message: { role: 'assistant' } };
|
|
153
|
+
|
|
154
|
+
const stream = await this.openai.chat.completions.create({
|
|
155
|
+
model: this.model,
|
|
156
|
+
messages: messages as any,
|
|
157
|
+
tools: tools as any,
|
|
158
|
+
tool_choice: 'auto',
|
|
159
|
+
stream: true,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
let contentBuffer = '';
|
|
163
|
+
const toolCallBuffers: Map<number, { id: string; name: string; arguments: string }> =
|
|
164
|
+
new Map();
|
|
165
|
+
|
|
166
|
+
for await (const chunk of stream) {
|
|
167
|
+
const delta = chunk.choices[0]?.delta;
|
|
168
|
+
|
|
169
|
+
// Handle content
|
|
170
|
+
if (delta?.content) {
|
|
171
|
+
contentBuffer += delta.content;
|
|
172
|
+
yield { type: 'content_delta', delta: { text: delta.content } };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Handle tool calls
|
|
176
|
+
if (delta?.tool_calls) {
|
|
177
|
+
for (const tc of delta.tool_calls) {
|
|
178
|
+
const idx = tc.index;
|
|
179
|
+
if (!toolCallBuffers.has(idx)) {
|
|
180
|
+
toolCallBuffers.set(idx, { id: '', name: '', arguments: '' });
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const buffer = toolCallBuffers.get(idx)!;
|
|
184
|
+
if (tc.id) buffer.id = tc.id;
|
|
185
|
+
if (tc.function?.name) buffer.name = tc.function.name;
|
|
186
|
+
if (tc.function?.arguments) buffer.arguments += tc.function.arguments;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Process tool calls if any
|
|
192
|
+
if (toolCallBuffers.size > 0) {
|
|
193
|
+
// Add assistant message to history
|
|
194
|
+
messages.push({
|
|
195
|
+
role: 'assistant',
|
|
196
|
+
content: contentBuffer,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
for (const [_, buffer] of toolCallBuffers) {
|
|
200
|
+
const args = JSON.parse(buffer.arguments);
|
|
201
|
+
|
|
202
|
+
yield {
|
|
203
|
+
type: 'tool_start',
|
|
204
|
+
tool: { id: buffer.id, name: buffer.name, arguments: args },
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// Execute the tool
|
|
208
|
+
const result = await this.executeTool(buffer.name, args);
|
|
209
|
+
|
|
210
|
+
if (result.startsWith('Error:')) {
|
|
211
|
+
yield { type: 'tool_end', tool: { id: buffer.id, error: result } };
|
|
212
|
+
} else {
|
|
213
|
+
yield { type: 'tool_end', tool: { id: buffer.id, result } };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Add tool result to messages
|
|
217
|
+
messages.push({
|
|
218
|
+
role: 'tool',
|
|
219
|
+
content: result,
|
|
220
|
+
tool_call_id: buffer.id,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
// No more tool calls - done
|
|
225
|
+
yield { type: 'message_end' };
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BaseClient - Abstract HTTP client for subagent APIs
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { BaseClientOptions } from './types';
|
|
6
|
+
|
|
7
|
+
export abstract class BaseClient {
|
|
8
|
+
protected apiUrl: string;
|
|
9
|
+
protected timeout: number;
|
|
10
|
+
|
|
11
|
+
constructor(options: BaseClientOptions = {}) {
|
|
12
|
+
this.apiUrl = options.apiUrl || this.getDefaultApiUrl();
|
|
13
|
+
this.timeout = options.timeout || 60000;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Override in subclasses to provide default API URL */
|
|
17
|
+
protected abstract getDefaultApiUrl(): string;
|
|
18
|
+
|
|
19
|
+
/** Make a GET request */
|
|
20
|
+
protected async get<T>(path: string): Promise<T> {
|
|
21
|
+
const response = await fetch(`${this.apiUrl}${path}`, {
|
|
22
|
+
method: 'GET',
|
|
23
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw new Error(`GET ${path} failed: ${response.statusText}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return response.json();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Make a POST request with JSON body */
|
|
34
|
+
protected async post<T>(path: string, body?: unknown): Promise<T> {
|
|
35
|
+
const response = await fetch(`${this.apiUrl}${path}`, {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: body ? { 'Content-Type': 'application/json' } : undefined,
|
|
38
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
39
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
throw new Error(`POST ${path} failed: ${response.statusText}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return response.json();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Make a POST request with FormData */
|
|
50
|
+
protected async postFormData<T>(path: string, formData: FormData): Promise<T> {
|
|
51
|
+
const response = await fetch(`${this.apiUrl}${path}`, {
|
|
52
|
+
method: 'POST',
|
|
53
|
+
body: formData,
|
|
54
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
throw new Error(`POST ${path} failed: ${response.statusText}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return response.json();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Make a DELETE request */
|
|
65
|
+
protected async delete(path: string): Promise<void> {
|
|
66
|
+
const response = await fetch(`${this.apiUrl}${path}`, {
|
|
67
|
+
method: 'DELETE',
|
|
68
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
throw new Error(`DELETE ${path} failed: ${response.statusText}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Download a file as Blob */
|
|
77
|
+
protected async downloadBlob(path: string): Promise<Blob> {
|
|
78
|
+
const response = await fetch(`${this.apiUrl}${path}`, {
|
|
79
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
throw new Error(`Download ${path} failed: ${response.statusText}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return response.blob();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Health check */
|
|
90
|
+
async health(): Promise<{ status: string; service: string }> {
|
|
91
|
+
return this.get('/health');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @morphllm/subagents/core
|
|
3
|
+
*
|
|
4
|
+
* Core utilities and base classes for building subagents.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { BaseClient } from './base-client';
|
|
8
|
+
export { BaseAgent } from './base-agent';
|
|
9
|
+
export type {
|
|
10
|
+
Tool,
|
|
11
|
+
ChatMessage,
|
|
12
|
+
ToolCall,
|
|
13
|
+
StreamEvent,
|
|
14
|
+
AgentRunResult,
|
|
15
|
+
BaseClientOptions,
|
|
16
|
+
BaseAgentOptions,
|
|
17
|
+
} from './types';
|