Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion examples/cli/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion examples/django_gemma/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file.
54 changes: 54 additions & 0 deletions examples/fastapi_openai/app/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""FastAPI + OpenRouter example.

WildEdge is injected via `wildedge run` before the app starts, so inference
tracking happens automatically for every chat.completions.create call.

Run with: see demo.sh
Requires: WILDEDGE_DSN and OPENROUTER_API_KEY environment variables.
"""

import os
import pathlib

from fastapi import FastAPI
from fastapi.responses import FileResponse
from openai import OpenAI
from pydantic import BaseModel

STATIC = pathlib.Path(__file__).parent / "static"

app = FastAPI()


@app.get("/")
def index() -> FileResponse:
return FileResponse(STATIC / "index.html")


client = OpenAI(
api_key=os.environ["OPENROUTER_API_KEY"],
base_url="https://openrouter.ai/api/v1",
)


class ChatRequest(BaseModel):
prompt: str
model: str = "openai/gpt-4o-mini"


class ChatResponse(BaseModel):
response: str
model: str


@app.post("/chat", response_model=ChatResponse)
def chat(req: ChatRequest) -> ChatResponse:
completion = client.chat.completions.create(
model=req.model,
messages=[{"role": "user", "content": req.prompt}],
max_tokens=512,
)
return ChatResponse(
response=completion.choices[0].message.content,
model=completion.model,
)
39 changes: 39 additions & 0 deletions examples/fastapi_openai/app/static/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>WildEdge + OpenRouter</title>
<style>
body { font-family: sans-serif; max-width: 640px; margin: 3rem auto; padding: 0 1rem; }
textarea { width: 100%; box-sizing: border-box; }
#response { white-space: pre-wrap; background: #f4f4f4; padding: 1rem; margin-top: 1rem; min-height: 3rem; }
#model-label { font-size: 0.8rem; color: #666; margin-top: 0.5rem; }
</style>
</head>
<body>
<h2>WildEdge + OpenRouter</h2>
<textarea id="prompt" rows="4" placeholder="Enter a prompt..."></textarea>
<br>
<label>Model: <input id="model" value="openai/gpt-4o-mini" size="30"></label>
<br><br>
<button onclick="send()">Send</button>
<div id="model-label"></div>
<div id="response"></div>
<script>
async function send() {
const prompt = document.getElementById("prompt").value.trim();
if (!prompt) return;
document.getElementById("response").textContent = "...";
document.getElementById("model-label").textContent = "";
const res = await fetch("/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ prompt, model: document.getElementById("model").value }),
});
const data = await res.json();
document.getElementById("response").textContent = data.response;
document.getElementById("model-label").textContent = "model: " + data.model;
}
</script>
</body>
</html>
37 changes: 37 additions & 0 deletions examples/fastapi_openai/demo.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
set -euo pipefail

if [[ -z "${WILDEDGE_DSN:-}" ]]; then
echo 'Set WILDEDGE_DSN first, e.g. export WILDEDGE_DSN="https://<secret>@ingest.wildedge.dev/<key>"' >&2
exit 1
fi

if [[ -z "${OPENROUTER_API_KEY:-}" ]]; then
echo 'Set OPENROUTER_API_KEY first, e.g. export OPENROUTER_API_KEY="sk-or-..."' >&2
exit 1
fi

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "${SCRIPT_DIR}"

uv sync

uv run wildedge doctor --integrations openai

# wildedge run execs uvicorn with wildedge/autoload/ prepended to PYTHONPATH.
# sitecustomize.py bootstraps the runtime before the app loads, instrumenting
# the OpenAI client for automatic inference tracking.
#
# --reload spawns a fresh worker process via exec each time code changes.
# The module-level guard in sitecustomize.py ensures that worker bootstraps
# correctly (unlike the old os.environ guard, which propagated across exec
# and blocked the worker's init).
uv run wildedge run \
--print-startup-report \
--integrations openai \
-- uvicorn app.main:app --reload --port 8000

# Test with:
# curl -s -X POST http://localhost:8000/chat \
# -H "Content-Type: application/json" \
# -d '{"prompt": "What is on-device AI in one sentence?"}' | jq .
13 changes: 13 additions & 0 deletions examples/fastapi_openai/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[project]
name = "wildedge-fastapi-openrouter"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = [
"wildedge-sdk",
"fastapi",
"uvicorn[standard]",
"openai",
]

[tool.uv.sources]
wildedge-sdk = { path = "../..", editable = true }
Loading
Loading