dánwòdánwò

AI

More model APIs

Gemini, Perplexity and Amazon Bedrock, answered from the same scripts as OpenAI and Anthropic, each in its own wire format.

The model API emulator answers three more APIs from the same scripts, each in its own shape: Google's Gemini API, Perplexity, and Amazon Bedrock's runtime. One script can serve all of them, or you can match on provider.

Set up

All three come with llmService; there's nothing to add. Each takes its key the way the real API does, and any key is accepted:

APIHostKey
Geminigenerativelanguage.googleapis.comx-goog-api-key or ?key=
Perplexityapi.perplexity.aia bearer token
Bedrockbedrock-runtime.<region>.amazonaws.coma well-formed SigV4 signature, or a Bedrock API key as a bearer token
danwo.config.ts
import { appEnvironment, defineConfig, llmService } from "@danwo/sdk"

export default defineConfig({
  testDir: "tests",
  environment: () =>
    appEnvironment({
      app: { command: "npm run dev", url: "http://localhost:3000" },
      services: [
        llmService({
          seed: {
            scripts: [
              {
                match: { provider: "perplexity" },
                reply: {
                  text: "Rates rose by a quarter point.",
                  sources: [{ url: "https://example.org/rates", title: "Rates" }],
                },
              },
              { match: { provider: "google" }, reply: { text: "Here's your plan for Monday." } },
            ],
          },
        }),
      ],
    }),
})

Seed it

The same seed as every model API; see Model APIs. Give a reply sources to have Perplexity cite them.

Use it in a test

Check the app shows Perplexity's citations:

tests/citations.danwo.ts
import { type LlmFixtures, llmCalls, test as base } from "@danwo/sdk"

const test = base.extend<LlmFixtures>()

test("cites its sources", async ({ app, screen, expect, llm }) => {
  await app.open("/research")
  await screen.getByLabel("Question").fill("What happened to rates?")
  await screen.getByRole("button", { name: "Research" }).click()

  await expect(screen.getByRole("link", { name: "Rates" })).toBeVisible()
  expect(llmCalls(llm).last()?.provider).toBe("perplexity")
})

What's covered

APICovered
GeminiGenerating text, function calls and structured output, streamed or not; token counts; embeddings.
PerplexityThe Agent API the AI SDK uses, and the Sonar API with citations and cost.
BedrockConverse and InvokeModel, streamed or not, in each model family's own body.

Faults

The same as every model API: 429, 500, 503, a long-context 400, a delay, a reset, an answer lost after the model answered, and the shaped answers: a refusal, an empty completion, a truncated answer, a content filter, a malformed tool call and a cut-off stream.

Fidelity

Offline. danwo fidelity checks Gemini (4 scenarios), Perplexity (3) and Bedrock (3) against the APIs' published descriptions, not live accounts.

Not covered yet

  • Gemini's Live API, file uploads and caching
  • Vertex AI's hosts
  • Bedrock Agents and knowledge bases
  • Recording Bedrock calls

Next

On this page

Run it on every pull request

A GitHub check and one comment per pull request, with the flight records attached.

Set up CI