Documentation Index
Fetch the complete documentation index at: https://docs.idyllic.so/llms.txt
Use this file to discover all available pages before exploring further.
This appendix provides a quick reference for Idyllic constructs used throughout the book. Idyllic is notation for exploring ideas—the principles transfer to any implementation.
Core Decorators
@field
Marks a property as persisted and client-synced state.
export default class MySystem extends AgenticSystem {
@field count = 0; // Primitive
@field items: string[] = []; // Array
@field config: Record<string, any> = {}; // Object
@field status: stream<string>(''); // Streaming value
}
Behavior:
- Automatically persisted across Durable Object hibernation
- Changes broadcast to all connected clients
- Supports all JSON-serializable types
@action()
Marks a method as callable from outside the system.
@action()
async processInput(input: string): Promise<string> {
// Implementation
return result;
}
Behavior:
- Callable via WebSocket from clients
- Typed parameters and return values
- Automatically generates client types
Marks a method as available to the LLM for invocation.
@tool({
description: 'Search the web for information on a topic'
})
async searchWeb(query: string): Promise<SearchResult[]> {
// Implementation
}
Parameters:
description: Explains what the tool does (shown to LLM)
- Optional:
permission: 'always' | 'confirm' | 'never'
@schedule()
Marks a method for periodic or scheduled execution.
@schedule('every 5 minutes')
async checkForUpdates() {
// Runs every 5 minutes
}
@schedule('every day at 2am')
async dailyMaintenance() {
// Runs once daily at 2 AM
}
Schedule formats:
'every N minutes'
'every N hours'
'every day at HH:MM'
'every monday at HH:MM'
Streaming Values
stream<T>()
Creates a streaming value that can be appended to incrementally.
@field output: stream<string>('');
@field items: stream<Item[]>([]);
// In a method:
this.output.append('chunk'); // Add to string
this.output.set('new value'); // Replace entirely
this.output.reset(''); // Clear to initial
const value = this.output.current(); // Get current value
this.items.set([...items, newItem]); // Update array
Use cases:
- Token-by-token LLM output
- Progress indicators
- Real-time logs
Vector Store
VectorStore<T>
Semantic storage with similarity search.
@field knowledge: VectorStore<{
content: string;
source: string;
timestamp: number;
}>;
// Insert
await this.knowledge.insert({
content: 'fact about topic',
source: 'documentation',
timestamp: Date.now()
});
// Search
const results = await this.knowledge.search(query, 5);
// Returns: { item: T, similarity: number }[]
// Get by ID
const item = await this.knowledge.get(id);
// Update
await this.knowledge.update(id, newItem);
// Delete
await this.knowledge.delete(id);
System Lifecycle
Initialization
export default class MySystem extends AgenticSystem {
async initialize() {
// Called once when system first created
// Set up initial state
}
}
State Access
// Access system ID (stable across restarts)
const id = this.id;
// Access environment
const apiKey = this.env.API_KEY;
State Synchronization
@action()
async longRunningTask() {
this.status = 'starting';
sync(); // Broadcast current state to clients
await this.doWork();
this.status = 'complete';
sync(); // Broadcast again
}
Client Integration
React Hook
import { useSystem } from '@idyllic/react';
function MyComponent() {
const {
count, // Field values
status, // Streaming value
increment, // Action method
isConnected, // Connection state
error // Any errors
} = useSystem<MySystemClient>();
return (
<div>
<p>Count: {count}</p>
<p>Status: {status}</p>
<button onClick={() => increment()}>+1</button>
</div>
);
}
Streaming Values in React
function StreamingOutput() {
const { output } = useSystem<MySystemClient>();
// output updates in real-time as tokens arrive
return <pre>{output}</pre>;
}
LLM Integration
Basic Completion
const response = await llm.complete([
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: prompt }
]);
const response = await llm.complete(messages, {
tools: [this.searchWeb, this.readFile]
});
// Response may include tool calls
if (response.toolCalls) {
for (const call of response.toolCalls) {
const result = await this[call.name](...call.arguments);
messages.push({
role: 'tool',
content: JSON.stringify(result),
toolCallId: call.id
});
}
}
Streaming Completion
for await (const chunk of llm.stream(messages)) {
this.output.append(chunk);
}
this.output.complete();
Complete Example
import { AgenticSystem, field, action, tool, schedule, stream, VectorStore } from '@idyllic/core';
export default class ResearchAssistant extends AgenticSystem {
// Persisted state
@field messages: Message[] = [];
@field knowledge: VectorStore<Knowledge>;
// Streaming output
@field thinking: stream<string>('');
@field response: stream<string>('');
// Configuration
@field systemPrompt = 'You are a research assistant...';
async initialize() {
this.knowledge = new VectorStore();
}
@action()
async chat(input: string) {
this.messages.push({ role: 'user', content: input });
this.thinking.reset('');
this.response.reset('');
// Retrieve relevant knowledge
const relevant = await this.knowledge.search(input, 5);
this.thinking.append('Found ' + relevant.length + ' relevant items\n');
// Generate response
for await (const chunk of llm.stream([
{ role: 'system', content: this.systemPrompt },
{ role: 'system', content: 'Relevant knowledge:\n' + relevant.map(r => r.item.content).join('\n') },
...this.messages
])) {
this.response.append(chunk);
}
this.messages.push({ role: 'assistant', content: this.response.current() });
}
@tool({ description: 'Search the web' })
async search(query: string): Promise<SearchResult[]> {
// Implementation
}
@schedule('every day at 3am')
async maintenance() {
// Clean up old messages
if (this.messages.length > 100) {
this.messages = this.messages.slice(-50);
}
}
}
interface Message {
role: 'user' | 'assistant' | 'system';
content: string;
}
interface Knowledge {
content: string;
source: string;
}
interface SearchResult {
title: string;
url: string;
snippet: string;
}