feat(ai,coding-agent): add faux provider and ModelRegistry factories

This commit is contained in:
Mario Zechner
2026-03-29 21:08:50 +02:00
parent fa890e3f94
commit ef6af5ebbd
31 changed files with 1425 additions and 94 deletions

View File

@@ -636,6 +636,92 @@ The library uses a registry of API implementations. Built-in APIs include:
- **`azure-openai-responses`**: Azure OpenAI Responses API (`streamAzureOpenAIResponses`, `AzureOpenAIResponsesOptions`)
- **`bedrock-converse-stream`**: Amazon Bedrock Converse API (`streamBedrock`, `BedrockOptions`)
### Faux provider for tests
`registerFauxProvider()` registers a temporary in-memory provider for tests and demos. It is opt-in and not part of the built-in provider set.
```typescript
import {
complete,
fauxAssistantMessage,
fauxText,
fauxThinking,
fauxToolCall,
registerFauxProvider,
stream,
} from '@mariozechner/pi-ai';
const registration = registerFauxProvider({
tokensPerSecond: 50 // optional
});
const model = registration.getModel();
const context = {
messages: [{ role: 'user', content: 'Summarize package.json and then call echo', timestamp: Date.now() }]
};
registration.setResponses([
fauxAssistantMessage([
fauxThinking('Need to inspect package metadata first.'),
fauxToolCall('echo', { text: 'package.json' })
], { stopReason: 'toolUse' })
]);
const first = await complete(model, context, {
sessionId: 'session-1',
cacheRetention: 'short'
});
context.messages.push(first);
context.messages.push({
role: 'toolResult',
toolCallId: first.content.find((block) => block.type === 'toolCall')!.id,
toolName: 'echo',
content: [{ type: 'text', text: 'package.json contents here' }],
isError: false,
timestamp: Date.now()
});
registration.setResponses([
fauxAssistantMessage([
fauxThinking('Now I can summarize the tool output.'),
fauxText('Here is the summary.')
])
]);
const s = stream(model, context);
for await (const event of s) {
console.log(event.type);
}
// Optional: register multiple faux models for model-switching tests
const multiModel = registerFauxProvider({
models: [
{ id: 'faux-fast', reasoning: false },
{ id: 'faux-thinker', reasoning: true }
]
});
const thinker = multiModel.getModel('faux-thinker');
console.log(thinker?.reasoning);
console.log(registration.getPendingResponseCount());
console.log(registration.state.callCount);
registration.unregister();
multiModel.unregister();
```
Notes:
- Responses are consumed from a queue in request start order.
- If the queue is empty, the faux provider returns an assistant error message with `errorMessage: "No more faux responses queued"`.
- Use `registration.setResponses([...])` to replace the remaining queue and `registration.appendResponses([...])` to add more responses.
- `registration.models` exposes all registered faux models. `registration.getModel()` returns the first one, and `registration.getModel(id)` returns a specific one.
- Use `fauxAssistantMessage(...)` for scripted assistant replies. Use `fauxText(...)`, `fauxThinking(...)`, and `fauxToolCall(...)` to build content blocks without filling in low-level fields manually.
- `registration.unregister()` removes the temporary provider from the global API registry.
- Usage is estimated at roughly 1 token per 4 characters. When `sessionId` is present and `cacheRetention` is not `"none"`, prompt cache reads and writes are simulated automatically.
- Tool call arguments stream incrementally via `toolcall_delta` chunks.
- By default, each streamed chunk is emitted on its own microtask. Set `tokensPerSecond` to pace chunk delivery in real time.
- The intended use is one deterministic scripted flow per registration. If you need independent concurrent flows, register separate faux providers.
### Providers and Models
A **provider** offers models through a specific API. For example: