yao/agent/context/JSAPI_OUTPUT.md

477 lines
8.6 KiB
Markdown

# Context Output JS API
The Context object now provides `Send`, `SendGroup`, and `Flush` methods directly for sending messages to clients from JavaScript.
## Usage
### ctx.Send(message)
Send a single message to the client.
**Parameters:**
- `message`: Can be a string (shorthand) or an object
**String Shorthand:**
```javascript
// Automatically converts to a text message
ctx.Send("Hello World");
```
**Object Format:**
```javascript
// Send text message
ctx.Send({
type: "text",
props: {
content: "Hello from JavaScript",
},
});
// Send loading message
ctx.Send({
type: "loading",
props: {
message: "Processing...",
},
});
// Send error message
ctx.Send({
type: "error",
props: {
message: "Something went wrong",
code: "ERR_500",
},
});
// Send custom message
ctx.Send({
type: "custom_widget",
props: {
data: { foo: "bar" },
},
});
```
**Complete Message Object:**
```javascript
ctx.Send({
type: "text",
props: {
content: "Hello",
},
id: "msg_123", // Optional: message ID
delta: true, // Optional: incremental update
done: false, // Optional: whether complete
delta_path: "content", // Optional: update path
delta_action: "append", // Optional: update action (append, replace, merge, set)
group_id: "grp_1", // Optional: message group ID
metadata: {
// Optional: metadata
timestamp: Date.now(),
sequence: 1,
trace_id: "trace_123",
},
});
```
### ctx.SendGroup(group)
Send a group of messages to the client.
**Parameters:**
- `group`: Message group object
**Example:**
```javascript
ctx.SendGroup({
id: "group_123",
messages: [
{
type: "text",
props: { content: "First message" },
},
{
type: "text",
props: { content: "Second message" },
},
],
metadata: {
timestamp: Date.now(),
},
});
```
### ctx.Flush()
Flush the output buffer to ensure all messages are sent to the client.
**Example:**
```javascript
ctx.Send("Processing...");
ctx.Flush(); // Send immediately
```
## Complete Examples
### Using in Hook Functions
```javascript
/**
* Create hook - called before assistant processes
*/
function Create(input, options) {
const ctx = input.context;
// Send welcome message
ctx.Send("Welcome to AI Assistant!");
// Send loading indicator
ctx.Send({
type: "loading",
props: {
message: "Thinking...",
},
});
return { messages: input.messages };
}
/**
* Done hook - called after assistant completes
*/
function Done(input, output) {
const ctx = input.context;
// Send completion message
ctx.Send({
type: "text",
props: {
content: "Processing completed!",
},
});
// Flush output
ctx.Flush();
return {};
}
```
### Streaming Response Example
```javascript
function StreamingResponse(input) {
const ctx = input.context;
// Send initial message
ctx.Send({
type: "text",
props: { content: "Starting process" },
id: "msg_1",
delta: false,
});
// Send incremental updates
ctx.Send({
type: "text",
props: { content: "..." },
id: "msg_1",
delta: true,
delta_path: "content",
delta_action: "append",
});
// Send completion marker
ctx.Send({
type: "text",
props: { content: "" },
id: "msg_1",
delta: false,
done: true,
});
ctx.Flush();
}
```
### Error Handling Example
```javascript
function ProcessWithErrorHandling(input) {
const ctx = input.context;
try {
// Processing logic
ctx.Send("Processing...");
// Simulate error
throw new Error("Something went wrong");
} catch (error) {
// Send error message
ctx.Send({
type: "error",
props: {
message: error.message,
code: "ERR_PROCESSING",
},
});
ctx.Flush();
}
}
```
### Multi-step Process Example
```javascript
function MultiStepProcess(input) {
const ctx = input.context;
// Step 1
ctx.Send({
type: "loading",
props: { message: "Step 1: Analyzing input..." },
});
ctx.Flush();
// ... processing ...
// Step 2
ctx.Send({
type: "loading",
props: { message: "Step 2: Generating response..." },
});
ctx.Flush();
// ... processing ...
// Final result
ctx.Send({
type: "text",
props: { content: "Process completed successfully!" },
});
ctx.Flush();
}
```
## Message Types
Built-in message types supported:
- `user_input` - User input (display only)
- `text` - Text content (supports Markdown)
- `thinking` - Reasoning/thinking process
- `loading` - Loading indicator
- `tool_call` - Tool/function call
- `error` - Error message
- `image` - Image content
- `audio` - Audio content
- `video` - Video content
- `action` - System action (silent in OpenAI clients)
- `event` - Lifecycle event (CUI only)
## Message Props by Type
### Text Message
```javascript
{
type: "text",
props: {
content: "Text content (supports Markdown)"
}
}
```
### Thinking Message
```javascript
{
type: "thinking",
props: {
content: "Reasoning process..."
}
}
```
### Loading Message
```javascript
{
type: "loading",
props: {
message: "Loading message..."
}
}
```
### Tool Call Message
```javascript
{
type: "tool_call",
props: {
id: "call_123",
name: "function_name",
arguments: '{"key": "value"}'
}
}
```
### Error Message
```javascript
{
type: "error",
props: {
message: "Error message",
code: "ERROR_CODE",
details: "Additional details"
}
}
```
### Image Message
```javascript
{
type: "image",
props: {
url: "https://example.com/image.jpg",
alt: "Image description",
width: 800,
height: 600
}
}
```
### Audio Message
```javascript
{
type: "audio",
props: {
url: "https://example.com/audio.mp3",
format: "mp3",
duration: 120.5,
transcript: "Audio transcript...",
autoplay: false,
controls: true
}
}
```
### Video Message
```javascript
{
type: "video",
props: {
url: "https://example.com/video.mp4",
format: "mp4",
thumbnail: "https://example.com/thumb.jpg",
width: 1920,
height: 1080,
autoplay: false,
controls: true
}
}
```
## Delta Updates
Use delta updates for streaming scenarios:
```javascript
// Initial message
ctx.Send({
type: "text",
props: { content: "Hello" },
id: "msg_1",
delta: false,
});
// Append to content
ctx.Send({
type: "text",
props: { content: " World" },
id: "msg_1",
delta: true,
delta_path: "content",
delta_action: "append",
});
// Mark as complete
ctx.Send({
type: "text",
props: {},
id: "msg_1",
done: true,
});
```
**Delta Actions:**
- `append` - Append to string or array
- `replace` - Replace value
- `merge` - Merge objects
- `set` - Set new field
## Notes
1. **No Separate Output API Needed**: The previous `const output = new Output(ctx)` approach is deprecated. Now use `ctx.Send()` methods directly.
2. **Automatic Client Handling**: Messages are automatically converted to the appropriate format based on `ctx.accept`:
- `standard` → OpenAI format
- `cui-web`/`cui-native`/`cui-desktop` → CUI native format
3. **Performance Optimization**: Output objects are automatically cached and managed, no manual management needed.
4. **Error Handling**: All methods throw JavaScript exceptions on failure, which can be caught with try-catch.
5. **Streaming Support**: Use delta updates with unique message IDs for real-time streaming scenarios.
6. **Metadata**: Optional metadata can be attached to messages for tracking, debugging, or custom processing.
## Migration Guide
**Before (Deprecated):**
```javascript
// Old way - no longer needed
const output = new Output(ctx)
output.Send("Hello")
output.SendGroup({ id: "grp1", messages: [...] })
```
**After (Current):**
```javascript
// New way - simpler and cleaner
ctx.Send("Hello")
ctx.SendGroup({ id: "grp1", messages: [...] })
ctx.Flush()
```
## Best Practices
1. **Use String Shorthand for Simple Messages**: `ctx.Send("Hello")` instead of `ctx.Send({ type: "text", props: { content: "Hello" } })`
2. **Always Flush After Important Messages**: Use `ctx.Flush()` to ensure messages are sent immediately
3. **Use Unique IDs for Delta Updates**: Assign unique IDs to messages that will receive incremental updates
4. **Handle Errors Gracefully**: Wrap Send operations in try-catch blocks for robust error handling
5. **Use Loading Indicators**: Show loading messages for long-running operations to improve UX
6. **Group Related Messages**: Use `SendGroup` for semantically related messages that should be displayed together