deploy
Deploy the qyl observability platform. Use when the user says "deploy", "deploy qyl", "push to production", "run qyl", or "go live".
SKILL.md
| Name | deploy |
| Description | Deploy the qyl observability platform. Use when the user says "deploy", "deploy qyl", "push to production", "run qyl", or "go live". |
qyl
Question Your Logs — AI Observability Platform
Landing page: https://ancplua.github.io/qyl/
What qyl Does
| Collects | OTLP receiver with idempotent ingestion (retry-safe) |
| Instruments | Roslyn source generators for zero-config GenAI telemetry |
| Visualizes | Real-time dashboard with SSE streaming |
| Integrates | MCP server and GitHub Copilot for AI agent observability |
Tech Stack
| Layer | Technology |
|---|---|
| Runtime | .NET 10.0 LTS, C# 14 |
| Frontend | React 19, Vite 7, Tailwind CSS 4 |
| Storage | DuckDB (columnar, upsert-based) |
| Protocol | OpenTelemetry 1.40 GenAI Semantic Conventions |
| Schema | TypeSpec → OpenAPI → C#/DuckDB/TypeScript |
Components
| Package | Purpose |
|---|---|
qyl.collector | OTLP receiver, DuckDB storage, REST API, embedded dashboard |
qyl.copilot | GitHub Copilot integration with AG-UI tool rendering |
qyl.hosting | App orchestration framework (QylRunner) |
qyl.servicedefaults | .NET instrumentation library with OTel setup |
qyl.servicedefaults.generator | Roslyn source generator for GenAI/DB interceptors |
qyl.instrumentation.generators | DuckDB insert + interceptor source generators |
qyl.mcp | MCP server for AI agent integration |
qyl.protocol | Shared types (BCL-only, no dependencies) |
Quick Start
Hosted
https://qyl-api-production.up.railway.app
Docker
docker build -f src/qyl.collector/Dockerfile -t qyl .
docker run -d -p 5100:5100 -p 4317:4317 -v ~/.qyl:/data qyl
From Source
git clone https://github.com/ANcpLua/qyl.git
cd qyl
dotnet run --project src/qyl.collector
Instrument Your .NET App
Add the service defaults package to automatically instrument GenAI calls:
// Program.cs
builder.AddQylServiceDefaults();
This auto-instruments:
IChatClientcalls (Microsoft.Extensions.AI)- Token usage, latency, model info
- Full OTel 1.40 GenAI semantic conventions
Set the exporter endpoint:
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:5100"
Use with Any OTel App
qyl accepts standard OTLP from any language/framework:
# Point any OpenTelemetry SDK at qyl
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:5100"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
Supported protocols:
- OTLP/HTTP (port 5100)
- OTLP/gRPC (port 4317)
Architecture
+------------------+ +------------------+
| Your .NET App | | Any OTel App |
| (servicedefaults)| | (Python, Go..) |
+--------+---------+ +--------+---------+
| |
| OTLP |
+----------------+-----------------------+
v
+----------------------------+
| qyl.hosting |
| (QylRunner) |
+-------------+--------------+
| orchestrates
v
+----------------------------+
| qyl.collector |
| (ASP.NET) |
+---+------+------+-----+---+
| | | |
+---------+ +---+---+ | +--+--------+
v v v | v v
+----------+ +----------+ | | +----------+ +----------------+
| DuckDB | | Dashboard| | | | MCP | | qyl.copilot |
| (storage)| | (React) | | | | (agents) | | (GitHub Copilot)|
+----------+ +----------+ | | +----------+ +-------+--------+
| | |
+-----+--+-----+ SSE / AG-UI
| Insights | |
| Materializer | v
+--------------+ GitHub Copilot
AG-UI Tool Rendering
qyl.copilot exposes observability tools to GitHub Copilot via SSE streaming with AG-UI event conventions.
Tools (via ObservabilityTools):
| Tool | Description |
|---|---|
search_spans | Search spans by service name, status, time range |
get_trace | Get all spans for a trace ID |
get_genai_stats | GenAI usage statistics (requests, tokens, costs) |
search_logs | Search logs by severity, body text, time range |
get_storage_stats | Storage statistics (span/log/session counts, size) |
list_sessions | List spans belonging to a session |
get_system_context | Pre-computed system context (zero query cost) |
Each tool is wrapped by DelegatingAIFunction which applies CopilotMetrics (counters, histograms) and
CopilotInstrumentation (OTel spans).
Endpoints:
| Method | Path | Purpose |
|---|---|---|
| POST | /api/v1/copilot/chat | Chat (SSE streaming) |
| GET | /api/v1/copilot/workflows | List workflows |
| POST | /api/v1/copilot/workflows/{name}/run | Execute workflow (SSE) |
| GET | /api/v1/copilot/status | Auth status |
| GET | /api/v1/copilot/executions | Execution history |
| GET | /api/v1/copilot/executions/{id} | Execution details |
SSE events use AG-UI convention: tool_call and tool_result event names.
Insights Materializer
Background service that pre-computes system context every 5 minutes (10-second warmup delay).
| Materializer | Computes |
|---|---|
TopologyMaterializer | Service discovery, AI model usage |
ProfileMaterializer | Latency percentiles (P50/P95/P99), token costs, trends |
AlertsMaterializer | Error spikes, cost drift, slow operations |
Results are stored in the materialized_insights table and served via get_system_context with zero query cost at read
time.
Ports
| Port | Protocol | Purpose |
|---|---|---|
| 5100 | HTTP | REST API, Dashboard, OTLP/HTTP |
| 4317 | gRPC | OTLP/gRPC ingestion |
Environment Variables
| Variable | Default | Purpose |
|---|---|---|
QYL_PORT | 5100 | HTTP API port |
QYL_GRPC_PORT | 4317 | gRPC OTLP port (0=disable) |
QYL_DATA_PATH | ./qyl.duckdb | DuckDB file location |
QYL_TOKEN | (none) | Auth token |
QYL_MAX_RETENTION_DAYS | 30 | Telemetry retention |
QYL_MAX_SPAN_COUNT | 1000000 | Max spans before cleanup |
QYL_MAX_LOG_COUNT | 500000 | Max logs before cleanup |
QYL_CLEANUP_INTERVAL_SECONDS | 300 | Cleanup interval |
QYL_OTLP_CORS_ALLOWED_ORIGINS | * | CORS origins (CSV) |
GenAI Telemetry
qyl captures OpenTelemetry 1.40 GenAI semantic conventions:
| Attribute | Description |
|---|---|
gen_ai.provider.name | Provider (openai, anthropic, etc) |
gen_ai.request.model | Model name |
gen_ai.usage.input_tokens | Prompt tokens |
gen_ai.usage.output_tokens | Completion tokens |
gen_ai.response.finish_reasons | Stop reason |
TypeSpec-First Design
All types are defined in TypeSpec and generated downstream:
core/specs/*.tsp
↓ (tsp compile)
core/openapi/openapi.yaml
↓ (nuke Generate)
┌───┴───┬───────┬────────┐
↓ ↓ ↓ ↓
C# DuckDB TS JSON Schema
Never edit *.g.cs or api.ts — edit TypeSpec and regenerate.
Idempotent Ingestion
Spans use ON CONFLICT (span_id) DO UPDATE — SDKs can safely retry on network errors without creating duplicates.
Mutable fields (tokens, status, cost) are updated; immutable fields (trace_id, name, start_time) are preserved.
Development
# Full build (TypeSpec → Docker)
nuke Full
# Regenerate types from TypeSpec
nuke Generate --force-generate
# Run tests
dotnet test
# Dashboard dev server (hot reload)
cd src/qyl.dashboard && npm run dev
# Collector only
dotnet run --project src/qyl.collector
Project Structure
core/ # TypeSpec schemas (source of truth)
eng/ # NUKE build system
src/
qyl.collector/ # Backend API service
qyl.copilot/ # GitHub Copilot integration (AG-UI)
qyl.dashboard/ # React frontend
qyl.hosting/ # App orchestration (QylRunner)
qyl.mcp/ # MCP server
qyl.protocol/ # Shared types (BCL-only)
qyl.servicedefaults/ # OTel instrumentation library
qyl.servicedefaults.generator/ # Roslyn source generator
qyl.instrumentation.generators/ # DuckDB insert + interceptor generators
tests/ # Test projects
License
MIT