This sample demonstrates how to build a streaming ASP.NET Core Web API using OpenRouter.NET with:
- Server-Sent Events (SSE) streaming
- Tool calling (calculator functions)
- Artifact support (code generation)
- Conversation management (multi-turn conversations)
The sample includes a CalculatorTools class with four operations:
- Add
- Subtract
- Multiply
- Divide
When users ask for calculations, the AI automatically invokes these tools and returns results via SSE.
The endpoint is configured to generate artifacts for:
- Code examples
- Scripts
- Deliverable content
Artifacts are automatically parsed and sent as SSE events (artifact_started, artifact_content, artifact_completed).
- Conversations are stored in-memory using
ConcurrentDictionary - Each conversation has a unique
conversationId - System prompt automatically added to new conversations
- Endpoint to clear conversations:
DELETE /conversation/{conversationId}
Stream a chat completion with tools and artifacts.
Request Body:
{
"message": "Your message here",
"model": "google/gemini-2.0-flash-exp", // Optional, defaults to gemini-2.0-flash-exp
"conversationId": "unique-id" // Optional, auto-generated if not provided
}Response: Server-Sent Events (SSE) stream with event types:
text- Text content deltastool_executing- Tool starting executiontool_completed- Tool finished with resulttool_error- Tool execution errorartifact_started- Artifact beginningartifact_content- Artifact content chunksartifact_completed- Complete artifactcompletion- Stream finishederror- Error occurred
Clear a conversation from the store.
Response:
{
"message": "Conversation cleared"
}- Set your OpenRouter API key in
Program.csline 24 (or better, use environment variables) - Run the application:
dotnet run
- Use the
.httpfile to test endpoints or connect with a client
curl -X POST http://localhost:5282/api/stream \
-H "Content-Type: application/json" \
-d '{"message": "What is 25 + 17?"}'curl -X POST http://localhost:5282/api/stream \
-H "Content-Type: application/json" \
-d '{"message": "Create a Python hello world function"}'# First message
curl -X POST http://localhost:5282/api/stream \
-H "Content-Type: application/json" \
-d '{"message": "Hello!", "conversationId": "conv-1"}'
# Follow-up
curl -X POST http://localhost:5282/api/stream \
-H "Content-Type: application/json" \
-d '{"message": "Calculate 10 * 5", "conversationId": "conv-1"}'const response = await fetch('/api/stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: 'What is 25 + 17?',
conversationId: 'my-conversation'
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n').filter(line => line.startsWith('data: '));
for (const line of lines) {
const json = line.substring(6);
const event = JSON.parse(json);
switch (event.type) {
case 'text':
console.log('Text:', event.content);
break;
case 'tool_completed':
console.log('Tool result:', event.result);
break;
case 'artifact_completed':
console.log('Artifact:', event.title, event.content);
break;
}
}
}All events follow this base structure:
{
"type": "text|tool_executing|tool_completed|artifact_started|...",
"chunkIndex": 0,
"elapsedMs": 123
}{
"type": "text",
"chunkIndex": 5,
"elapsedMs": 250,
"content": "Hello"
}{
"type": "tool_completed",
"chunkIndex": 10,
"elapsedMs": 500,
"toolName": "add",
"toolId": "call_abc123",
"arguments": "{\"a\":5,\"b\":3}",
"result": "8"
}{
"type": "artifact_completed",
"chunkIndex": 20,
"elapsedMs": 1000,
"artifactId": "artifact_xyz",
"artifactType": "code",
"title": "hello.py",
"language": "python",
"content": "def hello():\n print('Hello, World!')"
}- The
StreamAsSseAsyncextension handles all SSE formatting automatically - Tools are registered per-request (consider using dependency injection for production)
- Conversation history is stored in-memory (use a database for production)
- The default model is
google/gemini-2.0-flash-exp(fast and supports tools)