ai-functions 2.1.3 → 2.3.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +55 -1
- package/README.md +38 -0
- package/dist/ai-promise.d.ts +3 -3
- package/dist/ai-promise.d.ts.map +1 -1
- package/dist/ai-promise.js +135 -64
- package/dist/ai-promise.js.map +1 -1
- package/dist/ai-schemas.d.ts +56 -0
- package/dist/ai-schemas.d.ts.map +1 -0
- package/dist/ai-schemas.js +53 -0
- package/dist/ai-schemas.js.map +1 -0
- package/dist/ai.d.ts +16 -242
- package/dist/ai.d.ts.map +1 -1
- package/dist/ai.js +51 -858
- package/dist/ai.js.map +1 -1
- package/dist/batch/anthropic.d.ts +6 -4
- package/dist/batch/anthropic.d.ts.map +1 -1
- package/dist/batch/anthropic.js +83 -145
- package/dist/batch/anthropic.js.map +1 -1
- package/dist/batch/bedrock.d.ts +8 -30
- package/dist/batch/bedrock.d.ts.map +1 -1
- package/dist/batch/bedrock.js +155 -338
- package/dist/batch/bedrock.js.map +1 -1
- package/dist/batch/cloudflare.d.ts +8 -20
- package/dist/batch/cloudflare.d.ts.map +1 -1
- package/dist/batch/cloudflare.js +68 -189
- package/dist/batch/cloudflare.js.map +1 -1
- package/dist/batch/google.d.ts +6 -20
- package/dist/batch/google.d.ts.map +1 -1
- package/dist/batch/google.js +70 -238
- package/dist/batch/google.js.map +1 -1
- package/dist/batch/index.d.ts +4 -1
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js +4 -1
- package/dist/batch/index.js.map +1 -1
- package/dist/batch/memory.d.ts +1 -1
- package/dist/batch/memory.d.ts.map +1 -1
- package/dist/batch/memory.js +14 -10
- package/dist/batch/memory.js.map +1 -1
- package/dist/batch/openai.d.ts +11 -14
- package/dist/batch/openai.d.ts.map +1 -1
- package/dist/batch/openai.js +52 -156
- package/dist/batch/openai.js.map +1 -1
- package/dist/batch/provider.d.ts +111 -0
- package/dist/batch/provider.d.ts.map +1 -0
- package/dist/batch/provider.js +233 -0
- package/dist/batch/provider.js.map +1 -0
- package/dist/batch-map.d.ts.map +1 -1
- package/dist/batch-map.js +23 -17
- package/dist/batch-map.js.map +1 -1
- package/dist/batch-queue.d.ts +65 -0
- package/dist/batch-queue.d.ts.map +1 -1
- package/dist/batch-queue.js +169 -14
- package/dist/batch-queue.js.map +1 -1
- package/dist/budget.d.ts.map +1 -1
- package/dist/budget.js +27 -14
- package/dist/budget.js.map +1 -1
- package/dist/cache.d.ts +23 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +36 -15
- package/dist/cache.js.map +1 -1
- package/dist/context.d.ts +26 -8
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +64 -62
- package/dist/context.js.map +1 -1
- package/dist/digital-objects-registry.d.ts +229 -0
- package/dist/digital-objects-registry.d.ts.map +1 -0
- package/dist/digital-objects-registry.js +617 -0
- package/dist/digital-objects-registry.js.map +1 -0
- package/dist/embeddings.d.ts +2 -2
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +35 -0
- package/dist/errors.js.map +1 -0
- package/dist/eval/runner.d.ts +8 -0
- package/dist/eval/runner.d.ts.map +1 -1
- package/dist/eval/runner.js +41 -35
- package/dist/eval/runner.js.map +1 -1
- package/dist/eval-log/in-memory.d.ts +34 -0
- package/dist/eval-log/in-memory.d.ts.map +1 -0
- package/dist/eval-log/in-memory.js +84 -0
- package/dist/eval-log/in-memory.js.map +1 -0
- package/dist/eval-log/index.d.ts +29 -0
- package/dist/eval-log/index.d.ts.map +1 -0
- package/dist/eval-log/index.js +39 -0
- package/dist/eval-log/index.js.map +1 -0
- package/dist/eval-log/types.d.ts +101 -0
- package/dist/eval-log/types.d.ts.map +1 -0
- package/dist/eval-log/types.js +16 -0
- package/dist/eval-log/types.js.map +1 -0
- package/dist/function-registry.d.ts +116 -0
- package/dist/function-registry.d.ts.map +1 -0
- package/dist/function-registry.js +546 -0
- package/dist/function-registry.js.map +1 -0
- package/dist/generate.d.ts +9 -3
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +18 -18
- package/dist/generate.js.map +1 -1
- package/dist/index.d.ts +18 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -18
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +118 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +187 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware/budget.d.ts +84 -0
- package/dist/middleware/budget.d.ts.map +1 -0
- package/dist/middleware/budget.js +110 -0
- package/dist/middleware/budget.js.map +1 -0
- package/dist/middleware/cache.d.ts +103 -0
- package/dist/middleware/cache.d.ts.map +1 -0
- package/dist/middleware/cache.js +228 -0
- package/dist/middleware/cache.js.map +1 -0
- package/dist/middleware/embed-cache.d.ts +99 -0
- package/dist/middleware/embed-cache.d.ts.map +1 -0
- package/dist/middleware/embed-cache.js +128 -0
- package/dist/middleware/embed-cache.js.map +1 -0
- package/dist/middleware/index.d.ts +11 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +11 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/trace.d.ts +103 -0
- package/dist/middleware/trace.d.ts.map +1 -0
- package/dist/middleware/trace.js +176 -0
- package/dist/middleware/trace.js.map +1 -0
- package/dist/primitives.d.ts +120 -1
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +398 -26
- package/dist/primitives.js.map +1 -1
- package/dist/retry.d.ts +66 -1
- package/dist/retry.d.ts.map +1 -1
- package/dist/retry.js +115 -8
- package/dist/retry.js.map +1 -1
- package/dist/schema.js +2 -2
- package/dist/schema.js.map +1 -1
- package/dist/telemetry.d.ts +128 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +285 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/template.d.ts.map +1 -1
- package/dist/template.js +6 -1
- package/dist/template.js.map +1 -1
- package/dist/tool-orchestration.d.ts +66 -4
- package/dist/tool-orchestration.d.ts.map +1 -1
- package/dist/tool-orchestration.js +123 -23
- package/dist/tool-orchestration.js.map +1 -1
- package/dist/type-guards.d.ts +28 -0
- package/dist/type-guards.d.ts.map +1 -0
- package/dist/type-guards.js +29 -0
- package/dist/type-guards.js.map +1 -0
- package/dist/types.d.ts +135 -17
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +36 -1
- package/dist/types.js.map +1 -1
- package/dist/wrap-for-v3.d.ts +80 -0
- package/dist/wrap-for-v3.d.ts.map +1 -0
- package/dist/wrap-for-v3.js +89 -0
- package/dist/wrap-for-v3.js.map +1 -0
- package/examples/00-quickstart.ts +232 -0
- package/examples/01-rag-chatbot.ts +212 -0
- package/examples/02-multi-agent-research.ts +290 -0
- package/examples/03-email-classification.ts +379 -0
- package/examples/04-content-moderation.ts +400 -0
- package/examples/05-document-extraction.ts +455 -0
- package/examples/06-streaming-chat-nextjs.ts +437 -0
- package/examples/07-cloudflare-worker.ts +483 -0
- package/examples/08-batch-processing.ts +491 -0
- package/examples/09-budget-constrained.ts +527 -0
- package/examples/10-tool-orchestration.ts +565 -0
- package/examples/11-retry-resilience.ts +403 -0
- package/examples/12-caching-strategies.ts +422 -0
- package/examples/README.md +145 -0
- package/package.json +28 -25
- package/src/ai-promise.ts +226 -140
- package/src/ai-schemas.ts +122 -0
- package/src/ai.ts +69 -1176
- package/src/batch/anthropic.ts +96 -161
- package/src/batch/bedrock.ts +203 -454
- package/src/batch/cloudflare.ts +99 -282
- package/src/batch/google.ts +91 -297
- package/src/batch/index.ts +4 -1
- package/src/batch/memory.ts +15 -10
- package/src/batch/openai.ts +65 -193
- package/src/batch/provider.ts +336 -0
- package/src/batch-map.ts +29 -24
- package/src/batch-queue.ts +200 -11
- package/src/budget.ts +31 -18
- package/src/cache.ts +45 -17
- package/src/context.ts +106 -77
- package/src/digital-objects-registry.ts +750 -0
- package/src/errors.ts +37 -0
- package/src/eval/runner.ts +60 -36
- package/src/eval-log/in-memory.ts +90 -0
- package/src/eval-log/index.ts +46 -0
- package/src/eval-log/types.ts +110 -0
- package/src/function-registry.ts +671 -0
- package/src/generate.ts +33 -28
- package/src/index.ts +119 -21
- package/src/logger.ts +232 -0
- package/src/middleware/budget.ts +171 -0
- package/src/middleware/cache.ts +299 -0
- package/src/middleware/embed-cache.ts +195 -0
- package/src/middleware/index.ts +23 -0
- package/src/middleware/trace.ts +248 -0
- package/src/primitives.ts +589 -62
- package/src/retry.ts +144 -18
- package/src/schema.ts +8 -8
- package/src/telemetry.ts +403 -0
- package/src/template.ts +8 -4
- package/src/tool-orchestration.ts +213 -48
- package/src/type-guards.ts +31 -0
- package/src/types.ts +164 -25
- package/src/wrap-for-v3.ts +105 -0
- package/test/ai-promise.test.ts +1080 -0
- package/test/ai-proxy.test.ts +1 -1
- package/test/batch-autosubmit-errors.test.ts +49 -37
- package/test/batch-blog-posts.test.ts +87 -129
- package/test/core-functions.test.ts +183 -579
- package/test/decide.test.ts +154 -322
- package/test/define.test.ts +211 -8
- package/test/digital-objects-registry.test.ts +760 -0
- package/test/embedding-cache-middleware.test.ts +140 -0
- package/test/generate-core.test.ts +140 -229
- package/test/implicit-batch.test.ts +22 -65
- package/test/retry-policy-integration.test.ts +117 -0
- package/test/schema.test.ts +55 -19
- package/test/template.test.ts +1164 -0
- package/test/tool-orchestration.test.ts +270 -0
- package/test/wrap-for-v3.test.ts +612 -0
- package/vitest.config.js +6 -0
- package/vitest.config.ts +20 -0
- package/LICENSE +0 -21
- package/dist/rpc/auth.d.ts +0 -69
- package/dist/rpc/auth.d.ts.map +0 -1
- package/dist/rpc/auth.js +0 -136
- package/dist/rpc/auth.js.map +0 -1
- package/dist/rpc/client.d.ts +0 -62
- package/dist/rpc/client.d.ts.map +0 -1
- package/dist/rpc/client.js +0 -103
- package/dist/rpc/client.js.map +0 -1
- package/dist/rpc/deferred.d.ts +0 -60
- package/dist/rpc/deferred.d.ts.map +0 -1
- package/dist/rpc/deferred.js +0 -96
- package/dist/rpc/deferred.js.map +0 -1
- package/dist/rpc/index.d.ts +0 -22
- package/dist/rpc/index.d.ts.map +0 -1
- package/dist/rpc/index.js +0 -38
- package/dist/rpc/index.js.map +0 -1
- package/dist/rpc/local.d.ts +0 -42
- package/dist/rpc/local.d.ts.map +0 -1
- package/dist/rpc/local.js +0 -50
- package/dist/rpc/local.js.map +0 -1
- package/dist/rpc/server.d.ts +0 -165
- package/dist/rpc/server.d.ts.map +0 -1
- package/dist/rpc/server.js +0 -405
- package/dist/rpc/server.js.map +0 -1
- package/dist/rpc/session.d.ts +0 -32
- package/dist/rpc/session.d.ts.map +0 -1
- package/dist/rpc/session.js +0 -43
- package/dist/rpc/session.js.map +0 -1
- package/dist/rpc/transport.d.ts +0 -306
- package/dist/rpc/transport.d.ts.map +0 -1
- package/dist/rpc/transport.js +0 -731
- package/dist/rpc/transport.js.map +0 -1
- package/src/batch/anthropic.js +0 -256
- package/src/batch/bedrock.js +0 -584
- package/src/batch/cloudflare.js +0 -287
- package/src/batch/google.js +0 -359
- package/src/batch/index.js +0 -30
- package/src/batch/memory.js +0 -187
- package/src/batch/openai.js +0 -402
- package/src/eval/index.js +0 -7
- package/src/eval/models.js +0 -119
- package/src/eval/runner.js +0 -147
- package/test/schema.test.js +0 -96
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming Chat UI Example (Next.js Compatible)
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates building a streaming chat interface using ai-functions.
|
|
5
|
+
* It shows how to:
|
|
6
|
+
* - Stream responses in real-time
|
|
7
|
+
* - Handle partial object streaming
|
|
8
|
+
* - Integrate with frontend frameworks
|
|
9
|
+
* - Manage conversation state
|
|
10
|
+
*
|
|
11
|
+
* Note: This example simulates the streaming behavior. In a real Next.js app,
|
|
12
|
+
* you would use this in an API route or server action.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```bash
|
|
16
|
+
* ANTHROPIC_API_KEY=sk-... npx tsx examples/06-streaming-chat-nextjs.ts
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { ai, write, list, configure, AIPromise } from '../src/index.js'
|
|
21
|
+
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Types
|
|
24
|
+
// ============================================================================
|
|
25
|
+
|
|
26
|
+
interface Message {
|
|
27
|
+
id: string
|
|
28
|
+
role: 'user' | 'assistant'
|
|
29
|
+
content: string
|
|
30
|
+
timestamp: Date
|
|
31
|
+
streaming?: boolean
|
|
32
|
+
metadata?: {
|
|
33
|
+
model?: string
|
|
34
|
+
tokens?: number
|
|
35
|
+
latencyMs?: number
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface ChatState {
|
|
40
|
+
messages: Message[]
|
|
41
|
+
isStreaming: boolean
|
|
42
|
+
error?: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface StreamChunk {
|
|
46
|
+
type: 'text' | 'partial' | 'complete' | 'error'
|
|
47
|
+
content: string
|
|
48
|
+
partial?: unknown
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Chat Implementation
|
|
53
|
+
// ============================================================================
|
|
54
|
+
|
|
55
|
+
class StreamingChat {
|
|
56
|
+
private messages: Message[] = []
|
|
57
|
+
private model: string
|
|
58
|
+
private systemPrompt: string
|
|
59
|
+
|
|
60
|
+
constructor(options: { model?: string; systemPrompt?: string } = {}) {
|
|
61
|
+
this.model = options.model || 'sonnet'
|
|
62
|
+
this.systemPrompt =
|
|
63
|
+
options.systemPrompt ||
|
|
64
|
+
'You are a helpful assistant. Be concise but thorough in your responses.'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get conversation history
|
|
69
|
+
*/
|
|
70
|
+
getMessages(): Message[] {
|
|
71
|
+
return [...this.messages]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Add a user message
|
|
76
|
+
*/
|
|
77
|
+
addUserMessage(content: string): Message {
|
|
78
|
+
const message: Message = {
|
|
79
|
+
id: `msg-${Date.now()}`,
|
|
80
|
+
role: 'user',
|
|
81
|
+
content,
|
|
82
|
+
timestamp: new Date(),
|
|
83
|
+
}
|
|
84
|
+
this.messages.push(message)
|
|
85
|
+
return message
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Send a message and get a streaming response
|
|
90
|
+
*
|
|
91
|
+
* In Next.js, this would be used in an API route:
|
|
92
|
+
* ```ts
|
|
93
|
+
* // app/api/chat/route.ts
|
|
94
|
+
* export async function POST(req: Request) {
|
|
95
|
+
* const { message } = await req.json()
|
|
96
|
+
* const stream = chat.streamResponse(message)
|
|
97
|
+
*
|
|
98
|
+
* return new Response(stream, {
|
|
99
|
+
* headers: { 'Content-Type': 'text/event-stream' }
|
|
100
|
+
* })
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
async *streamResponse(userMessage: string): AsyncGenerator<StreamChunk> {
|
|
105
|
+
// Add user message
|
|
106
|
+
this.addUserMessage(userMessage)
|
|
107
|
+
|
|
108
|
+
// Build context from conversation history
|
|
109
|
+
const context = this.messages
|
|
110
|
+
.slice(-10) // Last 10 messages for context
|
|
111
|
+
.map((m) => `${m.role}: ${m.content}`)
|
|
112
|
+
.join('\n')
|
|
113
|
+
|
|
114
|
+
const startTime = Date.now()
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
// Create the AI prompt
|
|
118
|
+
const response = write`${this.systemPrompt}
|
|
119
|
+
|
|
120
|
+
Conversation history:
|
|
121
|
+
${context}
|
|
122
|
+
|
|
123
|
+
Respond to the user's last message naturally and helpfully.`
|
|
124
|
+
|
|
125
|
+
// Get the streaming interface
|
|
126
|
+
const stream = response.stream()
|
|
127
|
+
|
|
128
|
+
// Create placeholder assistant message
|
|
129
|
+
const assistantMessage: Message = {
|
|
130
|
+
id: `msg-${Date.now()}`,
|
|
131
|
+
role: 'assistant',
|
|
132
|
+
content: '',
|
|
133
|
+
timestamp: new Date(),
|
|
134
|
+
streaming: true,
|
|
135
|
+
}
|
|
136
|
+
this.messages.push(assistantMessage)
|
|
137
|
+
|
|
138
|
+
let fullContent = ''
|
|
139
|
+
|
|
140
|
+
// Stream text chunks
|
|
141
|
+
for await (const chunk of stream.textStream) {
|
|
142
|
+
fullContent += chunk
|
|
143
|
+
assistantMessage.content = fullContent
|
|
144
|
+
|
|
145
|
+
yield {
|
|
146
|
+
type: 'text',
|
|
147
|
+
content: chunk,
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Finalize message
|
|
152
|
+
assistantMessage.streaming = false
|
|
153
|
+
assistantMessage.metadata = {
|
|
154
|
+
model: this.model,
|
|
155
|
+
latencyMs: Date.now() - startTime,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
yield {
|
|
159
|
+
type: 'complete',
|
|
160
|
+
content: fullContent,
|
|
161
|
+
}
|
|
162
|
+
} catch (error) {
|
|
163
|
+
yield {
|
|
164
|
+
type: 'error',
|
|
165
|
+
content: (error as Error).message,
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get a non-streaming response (simpler for testing)
|
|
172
|
+
*/
|
|
173
|
+
async sendMessage(userMessage: string): Promise<Message> {
|
|
174
|
+
this.addUserMessage(userMessage)
|
|
175
|
+
|
|
176
|
+
const context = this.messages
|
|
177
|
+
.slice(-10)
|
|
178
|
+
.map((m) => `${m.role}: ${m.content}`)
|
|
179
|
+
.join('\n')
|
|
180
|
+
|
|
181
|
+
const startTime = Date.now()
|
|
182
|
+
|
|
183
|
+
const response = await write`${this.systemPrompt}
|
|
184
|
+
|
|
185
|
+
Conversation history:
|
|
186
|
+
${context}
|
|
187
|
+
|
|
188
|
+
Respond to the user's last message naturally and helpfully.`
|
|
189
|
+
|
|
190
|
+
const assistantMessage: Message = {
|
|
191
|
+
id: `msg-${Date.now()}`,
|
|
192
|
+
role: 'assistant',
|
|
193
|
+
content: response,
|
|
194
|
+
timestamp: new Date(),
|
|
195
|
+
metadata: {
|
|
196
|
+
model: this.model,
|
|
197
|
+
latencyMs: Date.now() - startTime,
|
|
198
|
+
},
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
this.messages.push(assistantMessage)
|
|
202
|
+
return assistantMessage
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Clear conversation
|
|
207
|
+
*/
|
|
208
|
+
clear(): void {
|
|
209
|
+
this.messages = []
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ============================================================================
|
|
214
|
+
// Structured Response Streaming
|
|
215
|
+
// ============================================================================
|
|
216
|
+
|
|
217
|
+
interface AnalysisResult {
|
|
218
|
+
summary: string
|
|
219
|
+
keyPoints: string[]
|
|
220
|
+
sentiment: string
|
|
221
|
+
confidence: number
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function streamStructuredResponse(text: string): Promise<void> {
|
|
225
|
+
console.log('\n--- Structured Response Streaming ---')
|
|
226
|
+
console.log('Analyzing text with streaming partial objects...\n')
|
|
227
|
+
|
|
228
|
+
const analysis = ai`Analyze this text:
|
|
229
|
+
"${text}"
|
|
230
|
+
|
|
231
|
+
Provide:
|
|
232
|
+
- summary: brief summary of the text
|
|
233
|
+
- keyPoints: array of 3-5 key points
|
|
234
|
+
- sentiment: positive/negative/neutral
|
|
235
|
+
- confidence: confidence score 0-1`
|
|
236
|
+
|
|
237
|
+
// Access properties to set schema
|
|
238
|
+
const { summary, keyPoints, sentiment, confidence } = analysis
|
|
239
|
+
|
|
240
|
+
const stream = analysis.stream()
|
|
241
|
+
|
|
242
|
+
console.log('Streaming partial objects:')
|
|
243
|
+
for await (const partial of stream.partialObjectStream) {
|
|
244
|
+
// Clear line and show current partial object
|
|
245
|
+
process.stdout.write('\r\x1b[K')
|
|
246
|
+
const p = partial as Partial<AnalysisResult>
|
|
247
|
+
const status = [
|
|
248
|
+
p.summary ? 'summary' : '',
|
|
249
|
+
p.keyPoints?.length ? `keyPoints(${p.keyPoints.length})` : '',
|
|
250
|
+
p.sentiment ? 'sentiment' : '',
|
|
251
|
+
p.confidence !== undefined ? 'confidence' : '',
|
|
252
|
+
]
|
|
253
|
+
.filter(Boolean)
|
|
254
|
+
.join(', ')
|
|
255
|
+
process.stdout.write(`Received: ${status}`)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const result = await stream.result
|
|
259
|
+
console.log('\n\nFinal result:', JSON.stringify(result, null, 2))
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// ============================================================================
|
|
263
|
+
// React Hooks Simulation (for documentation)
|
|
264
|
+
// ============================================================================
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Example React hook for streaming chat (for reference)
|
|
268
|
+
*
|
|
269
|
+
* ```tsx
|
|
270
|
+
* // hooks/useStreamingChat.ts
|
|
271
|
+
* import { useState, useCallback } from 'react'
|
|
272
|
+
*
|
|
273
|
+
* export function useStreamingChat() {
|
|
274
|
+
* const [messages, setMessages] = useState<Message[]>([])
|
|
275
|
+
* const [isStreaming, setIsStreaming] = useState(false)
|
|
276
|
+
*
|
|
277
|
+
* const sendMessage = useCallback(async (content: string) => {
|
|
278
|
+
* setIsStreaming(true)
|
|
279
|
+
*
|
|
280
|
+
* // Add user message
|
|
281
|
+
* const userMsg: Message = {
|
|
282
|
+
* id: Date.now().toString(),
|
|
283
|
+
* role: 'user',
|
|
284
|
+
* content,
|
|
285
|
+
* timestamp: new Date(),
|
|
286
|
+
* }
|
|
287
|
+
* setMessages(prev => [...prev, userMsg])
|
|
288
|
+
*
|
|
289
|
+
* // Create placeholder for assistant
|
|
290
|
+
* const assistantMsg: Message = {
|
|
291
|
+
* id: (Date.now() + 1).toString(),
|
|
292
|
+
* role: 'assistant',
|
|
293
|
+
* content: '',
|
|
294
|
+
* timestamp: new Date(),
|
|
295
|
+
* streaming: true,
|
|
296
|
+
* }
|
|
297
|
+
* setMessages(prev => [...prev, assistantMsg])
|
|
298
|
+
*
|
|
299
|
+
* // Stream from API
|
|
300
|
+
* const response = await fetch('/api/chat', {
|
|
301
|
+
* method: 'POST',
|
|
302
|
+
* body: JSON.stringify({ message: content }),
|
|
303
|
+
* })
|
|
304
|
+
*
|
|
305
|
+
* const reader = response.body?.getReader()
|
|
306
|
+
* const decoder = new TextDecoder()
|
|
307
|
+
*
|
|
308
|
+
* while (reader) {
|
|
309
|
+
* const { done, value } = await reader.read()
|
|
310
|
+
* if (done) break
|
|
311
|
+
*
|
|
312
|
+
* const chunk = decoder.decode(value)
|
|
313
|
+
* setMessages(prev => {
|
|
314
|
+
* const msgs = [...prev]
|
|
315
|
+
* const last = msgs[msgs.length - 1]
|
|
316
|
+
* if (last.role === 'assistant') {
|
|
317
|
+
* last.content += chunk
|
|
318
|
+
* }
|
|
319
|
+
* return msgs
|
|
320
|
+
* })
|
|
321
|
+
* }
|
|
322
|
+
*
|
|
323
|
+
* setIsStreaming(false)
|
|
324
|
+
* }, [])
|
|
325
|
+
*
|
|
326
|
+
* return { messages, isStreaming, sendMessage }
|
|
327
|
+
* }
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
|
|
331
|
+
// ============================================================================
|
|
332
|
+
// Terminal UI Simulation
|
|
333
|
+
// ============================================================================
|
|
334
|
+
|
|
335
|
+
function displayMessage(message: Message): void {
|
|
336
|
+
const roleColor = message.role === 'user' ? '\x1b[36m' : '\x1b[32m'
|
|
337
|
+
const reset = '\x1b[0m'
|
|
338
|
+
const prefix = message.role === 'user' ? 'You' : 'Assistant'
|
|
339
|
+
|
|
340
|
+
console.log(`\n${roleColor}${prefix}:${reset} ${message.content}`)
|
|
341
|
+
|
|
342
|
+
if (message.metadata?.latencyMs) {
|
|
343
|
+
console.log(`\x1b[90m (${message.metadata.latencyMs}ms)${reset}`)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async function simulateStreamingUI(chat: StreamingChat, message: string): Promise<void> {
|
|
348
|
+
console.log(`\n\x1b[36mYou:\x1b[0m ${message}`)
|
|
349
|
+
|
|
350
|
+
// Add user message
|
|
351
|
+
chat.addUserMessage(message)
|
|
352
|
+
|
|
353
|
+
// Build response like we would in the streaming method
|
|
354
|
+
const context = chat
|
|
355
|
+
.getMessages()
|
|
356
|
+
.slice(-10)
|
|
357
|
+
.map((m) => `${m.role}: ${m.content}`)
|
|
358
|
+
.join('\n')
|
|
359
|
+
|
|
360
|
+
process.stdout.write('\n\x1b[32mAssistant:\x1b[0m ')
|
|
361
|
+
|
|
362
|
+
const response = write`You are a helpful assistant. Be concise but thorough.
|
|
363
|
+
|
|
364
|
+
Conversation history:
|
|
365
|
+
${context}
|
|
366
|
+
|
|
367
|
+
Respond to the user's last message naturally and helpfully.`
|
|
368
|
+
|
|
369
|
+
// Since streaming requires gateway, we'll show non-streaming with simulated delay
|
|
370
|
+
const fullResponse = await response
|
|
371
|
+
|
|
372
|
+
// Simulate streaming output
|
|
373
|
+
for (const char of fullResponse) {
|
|
374
|
+
process.stdout.write(char)
|
|
375
|
+
await new Promise((r) => setTimeout(r, 10))
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
console.log('')
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// ============================================================================
|
|
382
|
+
// Main Example
|
|
383
|
+
// ============================================================================
|
|
384
|
+
|
|
385
|
+
async function main() {
|
|
386
|
+
console.log('\n=== Streaming Chat UI Example ===\n')
|
|
387
|
+
|
|
388
|
+
// Configure the AI provider
|
|
389
|
+
configure({
|
|
390
|
+
model: 'sonnet',
|
|
391
|
+
provider: 'anthropic',
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
// Create chat instance
|
|
395
|
+
const chat = new StreamingChat({
|
|
396
|
+
systemPrompt:
|
|
397
|
+
'You are a friendly and helpful assistant. Keep responses concise but informative.',
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
console.log('Simulating a streaming chat interface...')
|
|
401
|
+
console.log('(In Next.js, you would use Server-Sent Events or WebSockets)\n')
|
|
402
|
+
|
|
403
|
+
// Simulate a conversation
|
|
404
|
+
const conversation = [
|
|
405
|
+
'What are the key features of ai-functions?',
|
|
406
|
+
'How does streaming work?',
|
|
407
|
+
'Can you give me a code example?',
|
|
408
|
+
]
|
|
409
|
+
|
|
410
|
+
for (const message of conversation) {
|
|
411
|
+
await simulateStreamingUI(chat, message)
|
|
412
|
+
console.log('')
|
|
413
|
+
await new Promise((r) => setTimeout(r, 500))
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Show structured streaming
|
|
417
|
+
await streamStructuredResponse(
|
|
418
|
+
'The new product launch exceeded expectations with a 40% increase in sales.'
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
// Display conversation summary
|
|
422
|
+
console.log('\n--- Conversation Summary ---')
|
|
423
|
+
const messages = chat.getMessages()
|
|
424
|
+
console.log(`Total messages: ${messages.length}`)
|
|
425
|
+
console.log(`User messages: ${messages.filter((m) => m.role === 'user').length}`)
|
|
426
|
+
console.log(`Assistant messages: ${messages.filter((m) => m.role === 'assistant').length}`)
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
main()
|
|
430
|
+
.then(() => {
|
|
431
|
+
console.log('\n=== Example Complete ===\n')
|
|
432
|
+
process.exit(0)
|
|
433
|
+
})
|
|
434
|
+
.catch((error) => {
|
|
435
|
+
console.error('\nError:', error.message)
|
|
436
|
+
process.exit(1)
|
|
437
|
+
})
|