diff --git a/README.md b/README.md index 126e8569..ed4af0c4 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ Or have a look at the general catalog below: |-----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | AWS Lambda + CDK | [](typescript/integrations/deployment-lambda-cdk) [](go/integrations/go-lambda-cdk) [](java/integrations/java-gradle-lambda-cdk) [](kotlin/integrations/kotlin-gradle-lambda-cdk) | | XState | [](typescript/integrations/xstate) | +| Tuning Engines AI Gateway | [](typescript/integrations/tuning-engines-governed-ai) | | Knative | [](go/integrations/knative-go) | #### End-to-End Applications @@ -172,4 +173,4 @@ git push origin v0.9.1 This triggers a workflow that [creates a draft release](https://github.com/restatedev/examples/releases) on GitHub, which you need to approve to finalize it. -Please update the version tag referenced on the [Tour of Restate](https://github.com/restatedev/documentation) documentation page. \ No newline at end of file +Please update the version tag referenced on the [Tour of Restate](https://github.com/restatedev/documentation) documentation page. diff --git a/typescript/integrations/tuning-engines-governed-ai/README.md b/typescript/integrations/tuning-engines-governed-ai/README.md new file mode 100644 index 00000000..e4f3dfb8 --- /dev/null +++ b/typescript/integrations/tuning-engines-governed-ai/README.md @@ -0,0 +1,44 @@ +# Tuning Engines governed AI with Restate + +This example shows a Restate service handler calling the Tuning Engines +OpenAI-compatible endpoint. Restate owns durable execution and retries; Tuning +Engines owns model routing, policy checks, budgets, approvals, traces, and +runtime state references. + +## Setup + +```bash +npm install +export TE_INFERENCE_KEY=sk-te-your-inference-key +export TE_MODEL=auto +``` + +## Run + +Start Restate locally: + +```bash +npx @restatedev/restate-server +``` + +Start the service: + +```bash +npm run service +``` + +Register the service: + +```bash +npx @restatedev/restate deployments register http://localhost:9080 +``` + +Invoke the handler using the normal Restate CLI or SDK flow. The handler passes +`run_id` and `request_id` metadata so Tuning Engines can correlate model usage, +policy decisions, approvals, and traces back to the Restate invocation. + +## Files + +- `src/app.ts` - Restate service with a governed model call. +- `package.json` - Minimal TypeScript project setup. +- `tsconfig.json` - TypeScript compiler settings. diff --git a/typescript/integrations/tuning-engines-governed-ai/package.json b/typescript/integrations/tuning-engines-governed-ai/package.json new file mode 100644 index 00000000..d6fa4704 --- /dev/null +++ b/typescript/integrations/tuning-engines-governed-ai/package.json @@ -0,0 +1,17 @@ +{ + "name": "restate-tuning-engines-governed-ai", + "private": true, + "type": "module", + "scripts": { + "service": "tsx src/app.ts", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@restatedev/restate-sdk": "^1.14.4", + "tsx": "^4.20.6", + "typescript": "^5.9.3" + }, + "devDependencies": { + "@types/node": "^24.10.1" + } +} diff --git a/typescript/integrations/tuning-engines-governed-ai/src/app.ts b/typescript/integrations/tuning-engines-governed-ai/src/app.ts new file mode 100644 index 00000000..ba926d8d --- /dev/null +++ b/typescript/integrations/tuning-engines-governed-ai/src/app.ts @@ -0,0 +1,61 @@ +import * as restate from "@restatedev/restate-sdk"; + +type Input = { + prompt?: string; + run_id?: string; +}; + +function newId(prefix: string): string { + return `${prefix}_${crypto.randomUUID().replaceAll("-", "")}`; +} + +async function callTuningEngines(input: { + prompt: string; + run_id: string; + request_id: string; +}): Promise { + const key = process.env.TE_INFERENCE_KEY; + if (!key) { + throw new Error("Set TE_INFERENCE_KEY before invoking this service."); + } + + const response = await fetch("https://api.tuningengines.com/v1/chat/completions", { + method: "POST", + headers: { + Authorization: `Bearer ${key}`, + "Content-Type": "application/json", + "X-TE-Run-ID": input.run_id, + "X-TE-Request-ID": input.request_id, + }, + body: JSON.stringify({ + model: process.env.TE_MODEL || "auto", + messages: [{ role: "user", content: input.prompt }], + metadata: { + run_id: input.run_id, + request_id: input.request_id, + runtime: "restate", + event_type: "model.call", + }, + }), + }); + + if (!response.ok) { + throw new Error(`Tuning Engines request failed: ${response.status} ${await response.text()}`); + } + + return response.json(); +} + +export default restate.service({ + name: "TuningEnginesService", + handlers: { + async governedAi(_ctx: restate.Context, input: Input) { + const run_id = input.run_id || newId("restate"); + const request_id = newId("req"); + const prompt = input.prompt || "Say hello from a governed Restate handler."; + + const result = await callTuningEngines({ prompt, run_id, request_id }); + return { run_id, request_id, result }; + }, + }, +}); diff --git a/typescript/integrations/tuning-engines-governed-ai/tsconfig.json b/typescript/integrations/tuning-engines-governed-ai/tsconfig.json new file mode 100644 index 00000000..414e96a7 --- /dev/null +++ b/typescript/integrations/tuning-engines-governed-ai/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true + }, + "include": ["src/**/*.ts"] +}