@cuylabs/agent-http 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 +104 -0
- package/dist/index.d.ts +89 -0
- package/dist/index.js +167 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# @cuylabs/agent-http
|
|
2
|
+
|
|
3
|
+
HTTP streaming adapter for [@cuylabs/agent-core](https://github.com/cuylabs-ai/agents-ts/tree/main/packages/agent-core). Bridges agent events to AI SDK v6 UIMessageStream for use with `useChat()`.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @cuylabs/agent-http @cuylabs/agent-core
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @cuylabs/agent-http @cuylabs/agent-core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
You'll also need at least one AI SDK provider:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @ai-sdk/openai
|
|
17
|
+
# or @ai-sdk/anthropic, @ai-sdk/google
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// app/api/chat/route.ts (Next.js)
|
|
24
|
+
import { createAgent } from "@cuylabs/agent-core";
|
|
25
|
+
import { createAgentStreamResponse } from "@cuylabs/agent-http";
|
|
26
|
+
import { anthropic } from "@ai-sdk/anthropic";
|
|
27
|
+
|
|
28
|
+
const agent = createAgent({
|
|
29
|
+
model: anthropic("claude-sonnet-4-20250514"),
|
|
30
|
+
systemPrompt: "You are a helpful assistant.",
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export async function POST(req: Request) {
|
|
34
|
+
const { messages, id } = await req.json();
|
|
35
|
+
const lastMessage = messages[messages.length - 1];
|
|
36
|
+
|
|
37
|
+
return createAgentStreamResponse(agent, {
|
|
38
|
+
sessionId: id,
|
|
39
|
+
message: lastMessage.content,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then use with AI SDK's `useChat()`:
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
"use client";
|
|
48
|
+
import { useChat } from "@ai-sdk/react";
|
|
49
|
+
|
|
50
|
+
export function Chat() {
|
|
51
|
+
const { messages, input, handleInputChange, handleSubmit } = useChat({
|
|
52
|
+
api: "/api/chat",
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div>
|
|
57
|
+
{messages.map((m) => (
|
|
58
|
+
<div key={m.id}>
|
|
59
|
+
{m.role}: {m.content}
|
|
60
|
+
</div>
|
|
61
|
+
))}
|
|
62
|
+
<form onSubmit={handleSubmit}>
|
|
63
|
+
<input value={input} onChange={handleInputChange} />
|
|
64
|
+
<button type="submit">Send</button>
|
|
65
|
+
</form>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## API
|
|
72
|
+
|
|
73
|
+
### createAgentStreamResponse
|
|
74
|
+
|
|
75
|
+
Creates an HTTP Response with streaming agent events.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
function createAgentStreamResponse(
|
|
79
|
+
agent: Agent,
|
|
80
|
+
options: AgentStreamOptions
|
|
81
|
+
): Response;
|
|
82
|
+
|
|
83
|
+
interface AgentStreamOptions {
|
|
84
|
+
sessionId: string;
|
|
85
|
+
message: string;
|
|
86
|
+
abortSignal?: AbortSignal;
|
|
87
|
+
system?: string;
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### createAgentStream
|
|
92
|
+
|
|
93
|
+
Lower-level API that returns a ReadableStream.
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
function createAgentStream(
|
|
97
|
+
agent: Agent,
|
|
98
|
+
options: AgentStreamOptions
|
|
99
|
+
): ReturnType<typeof createUIMessageStream>;
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
Apache-2.0
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Agent } from '@cuylabs/agent-core';
|
|
2
|
+
export { AgentEvent } from '@cuylabs/agent-core';
|
|
3
|
+
import { createUIMessageStream } from 'ai';
|
|
4
|
+
export { createUIMessageStream, createUIMessageStreamResponse } from 'ai';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @cuylabs/agent-http
|
|
8
|
+
*
|
|
9
|
+
* HTTP streaming adapter for @cuylabs/agent-core.
|
|
10
|
+
* Bridges agent events to AI SDK v6 compatible UIMessageStream for use with useChat().
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // In a Next.js API route
|
|
15
|
+
* import { createAgent } from "@cuylabs/agent-core";
|
|
16
|
+
* import { createAgentStreamResponse } from "@cuylabs/agent-http";
|
|
17
|
+
* import { anthropic } from "@ai-sdk/anthropic";
|
|
18
|
+
*
|
|
19
|
+
* const agent = createAgent({
|
|
20
|
+
* model: anthropic("claude-sonnet-4-20250514"),
|
|
21
|
+
* systemPrompt: "You are a helpful assistant.",
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* export async function POST(req: Request) {
|
|
25
|
+
* const { messages, id } = await req.json();
|
|
26
|
+
* const lastMessage = messages[messages.length - 1];
|
|
27
|
+
*
|
|
28
|
+
* return createAgentStreamResponse(agent, {
|
|
29
|
+
* sessionId: id,
|
|
30
|
+
* message: lastMessage.content,
|
|
31
|
+
* });
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @packageDocumentation
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Options for creating an agent stream response
|
|
40
|
+
*/
|
|
41
|
+
interface AgentStreamOptions {
|
|
42
|
+
/** Session ID for conversation history */
|
|
43
|
+
sessionId: string;
|
|
44
|
+
/** User message to send */
|
|
45
|
+
message: string;
|
|
46
|
+
/** Optional abort signal */
|
|
47
|
+
abortSignal?: AbortSignal;
|
|
48
|
+
/** Optional system prompt override for this message */
|
|
49
|
+
system?: string;
|
|
50
|
+
/** Callback when streaming completes - use for persistence */
|
|
51
|
+
onFinish?: (result: {
|
|
52
|
+
response: string;
|
|
53
|
+
messageId?: string;
|
|
54
|
+
}) => void | Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create an HTTP streaming response from an agent.
|
|
58
|
+
*
|
|
59
|
+
* This bridges @cuylabs/agent-core's event-based streaming to the
|
|
60
|
+
* AI SDK v6 UIMessageStream format that useChat() understands.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* // Next.js API route
|
|
65
|
+
* export async function POST(req: Request) {
|
|
66
|
+
* const { messages, id } = await req.json();
|
|
67
|
+
* const lastMessage = messages[messages.length - 1];
|
|
68
|
+
*
|
|
69
|
+
* return createAgentStreamResponse(agent, {
|
|
70
|
+
* sessionId: id,
|
|
71
|
+
* message: lastMessage.content,
|
|
72
|
+
* onFinish: async ({ response }) => {
|
|
73
|
+
* // Persist the response to database
|
|
74
|
+
* await saveMessage({ chatId: id, content: response, role: "assistant" });
|
|
75
|
+
* },
|
|
76
|
+
* });
|
|
77
|
+
* }
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
declare function createAgentStreamResponse(agent: Agent, options: AgentStreamOptions): Response;
|
|
81
|
+
/**
|
|
82
|
+
* Create a ReadableStream of UIMessageChunks from agent events.
|
|
83
|
+
*
|
|
84
|
+
* Lower-level than createAgentStreamResponse - useful when you need
|
|
85
|
+
* to compose streams or add custom middleware.
|
|
86
|
+
*/
|
|
87
|
+
declare function createAgentStream(agent: Agent, options: AgentStreamOptions): ReturnType<typeof createUIMessageStream>;
|
|
88
|
+
|
|
89
|
+
export { type AgentStreamOptions, createAgentStream, createAgentStreamResponse };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import {
|
|
3
|
+
createUIMessageStream,
|
|
4
|
+
createUIMessageStreamResponse
|
|
5
|
+
} from "ai";
|
|
6
|
+
var idCounter = 0;
|
|
7
|
+
function generatePartId() {
|
|
8
|
+
return `part-${Date.now()}-${++idCounter}`;
|
|
9
|
+
}
|
|
10
|
+
async function writeAgentEventsToStream(agent, options, writer) {
|
|
11
|
+
let currentTextId = null;
|
|
12
|
+
let currentReasoningId = null;
|
|
13
|
+
let fullResponse = "";
|
|
14
|
+
for await (const event of agent.chat(options.sessionId, options.message, {
|
|
15
|
+
abort: options.abortSignal,
|
|
16
|
+
system: options.system
|
|
17
|
+
})) {
|
|
18
|
+
switch (event.type) {
|
|
19
|
+
// Text streaming
|
|
20
|
+
case "text-start":
|
|
21
|
+
currentTextId = generatePartId();
|
|
22
|
+
writer.write({
|
|
23
|
+
type: "text-start",
|
|
24
|
+
id: currentTextId
|
|
25
|
+
});
|
|
26
|
+
break;
|
|
27
|
+
case "text-delta":
|
|
28
|
+
if (currentTextId) {
|
|
29
|
+
fullResponse += event.text;
|
|
30
|
+
writer.write({
|
|
31
|
+
type: "text-delta",
|
|
32
|
+
id: currentTextId,
|
|
33
|
+
delta: event.text
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
break;
|
|
37
|
+
case "text-end":
|
|
38
|
+
if (currentTextId) {
|
|
39
|
+
writer.write({
|
|
40
|
+
type: "text-end",
|
|
41
|
+
id: currentTextId
|
|
42
|
+
});
|
|
43
|
+
currentTextId = null;
|
|
44
|
+
}
|
|
45
|
+
break;
|
|
46
|
+
// Reasoning/thinking streaming
|
|
47
|
+
case "reasoning-start":
|
|
48
|
+
currentReasoningId = generatePartId();
|
|
49
|
+
writer.write({
|
|
50
|
+
type: "reasoning-start",
|
|
51
|
+
id: currentReasoningId
|
|
52
|
+
});
|
|
53
|
+
break;
|
|
54
|
+
case "reasoning-delta":
|
|
55
|
+
if (currentReasoningId) {
|
|
56
|
+
writer.write({
|
|
57
|
+
type: "reasoning-delta",
|
|
58
|
+
id: currentReasoningId,
|
|
59
|
+
delta: event.text
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
break;
|
|
63
|
+
case "reasoning-end":
|
|
64
|
+
if (currentReasoningId) {
|
|
65
|
+
writer.write({
|
|
66
|
+
type: "reasoning-end",
|
|
67
|
+
id: currentReasoningId
|
|
68
|
+
});
|
|
69
|
+
currentReasoningId = null;
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
// Tool invocations
|
|
73
|
+
case "tool-start":
|
|
74
|
+
writer.write({
|
|
75
|
+
type: "tool-input-available",
|
|
76
|
+
toolCallId: event.toolCallId,
|
|
77
|
+
toolName: event.toolName,
|
|
78
|
+
input: event.input
|
|
79
|
+
});
|
|
80
|
+
break;
|
|
81
|
+
case "tool-result":
|
|
82
|
+
writer.write({
|
|
83
|
+
type: "tool-output-available",
|
|
84
|
+
toolCallId: event.toolCallId,
|
|
85
|
+
output: event.result
|
|
86
|
+
});
|
|
87
|
+
break;
|
|
88
|
+
case "tool-error":
|
|
89
|
+
writer.write({
|
|
90
|
+
type: "tool-output-available",
|
|
91
|
+
toolCallId: event.toolCallId,
|
|
92
|
+
output: { error: event.error, isError: true }
|
|
93
|
+
});
|
|
94
|
+
break;
|
|
95
|
+
// Error handling
|
|
96
|
+
case "error":
|
|
97
|
+
writer.write({
|
|
98
|
+
type: "error",
|
|
99
|
+
errorText: event.error.message
|
|
100
|
+
});
|
|
101
|
+
break;
|
|
102
|
+
// Step and message lifecycle events - no direct mapping in AI SDK v6
|
|
103
|
+
// These can be exposed via custom data parts if needed
|
|
104
|
+
case "step-start":
|
|
105
|
+
case "step-finish":
|
|
106
|
+
case "message":
|
|
107
|
+
case "complete":
|
|
108
|
+
break;
|
|
109
|
+
// Events we don't need to stream
|
|
110
|
+
case "status":
|
|
111
|
+
case "retry":
|
|
112
|
+
case "doom-loop":
|
|
113
|
+
case "context-overflow":
|
|
114
|
+
case "computer-call":
|
|
115
|
+
case "computer-result":
|
|
116
|
+
case "approval-request":
|
|
117
|
+
case "approval-resolved":
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return fullResponse;
|
|
122
|
+
}
|
|
123
|
+
function createAgentStreamResponse(agent, options) {
|
|
124
|
+
let fullResponse = "";
|
|
125
|
+
const stream = createUIMessageStream({
|
|
126
|
+
execute: async ({ writer }) => {
|
|
127
|
+
fullResponse = await writeAgentEventsToStream(agent, options, writer);
|
|
128
|
+
},
|
|
129
|
+
onFinish: async ({ responseMessage }) => {
|
|
130
|
+
if (options.onFinish) {
|
|
131
|
+
await options.onFinish({
|
|
132
|
+
response: fullResponse,
|
|
133
|
+
messageId: responseMessage?.id
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
onError: (error) => {
|
|
138
|
+
return error instanceof Error ? error.message : "Unknown error";
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
return createUIMessageStreamResponse({ stream });
|
|
142
|
+
}
|
|
143
|
+
function createAgentStream(agent, options) {
|
|
144
|
+
let fullResponse = "";
|
|
145
|
+
return createUIMessageStream({
|
|
146
|
+
execute: async ({ writer }) => {
|
|
147
|
+
fullResponse = await writeAgentEventsToStream(agent, options, writer);
|
|
148
|
+
},
|
|
149
|
+
onFinish: async ({ responseMessage }) => {
|
|
150
|
+
if (options.onFinish) {
|
|
151
|
+
await options.onFinish({
|
|
152
|
+
response: fullResponse,
|
|
153
|
+
messageId: responseMessage?.id
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
onError: (error) => {
|
|
158
|
+
return error instanceof Error ? error.message : "Unknown error";
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
export {
|
|
163
|
+
createAgentStream,
|
|
164
|
+
createAgentStreamResponse,
|
|
165
|
+
createUIMessageStream,
|
|
166
|
+
createUIMessageStreamResponse
|
|
167
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cuylabs/agent-http",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "HTTP streaming adapter for @cuylabs/agent-core - bridges agent events to AI SDK compatible streams",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
21
|
+
"dev": "tsup src/index.ts --format esm --dts --watch",
|
|
22
|
+
"typecheck": "tsc --noEmit",
|
|
23
|
+
"clean": "rm -rf dist"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@cuylabs/agent-core": "workspace:*"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^22.0.0",
|
|
30
|
+
"tsup": "^8.0.0",
|
|
31
|
+
"typescript": "^5.7.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"ai": "^6.0.0"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"agent",
|
|
38
|
+
"ai",
|
|
39
|
+
"http",
|
|
40
|
+
"streaming",
|
|
41
|
+
"vercel-ai-sdk"
|
|
42
|
+
],
|
|
43
|
+
"author": "cuylabs",
|
|
44
|
+
"license": "Apache-2.0",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/cuylabs-ai/agents-ts.git",
|
|
48
|
+
"directory": "packages/agent-http"
|
|
49
|
+
}
|
|
50
|
+
}
|