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
5 changes: 3 additions & 2 deletions cli/commands/push/command.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3789,11 +3789,12 @@ describe("push divergence guard", () => {
assertEquals((error as Error & { slug?: string }).slug, "push-conflict");
assertStringIncludes(error.message, '"app.ts"');
assertEquals(fileListCalls, 3);
assertEquals(putPaths, [
// Directory iteration order varies by filesystem; verify every attempted write.
assertEquals(putPaths.toSorted(), [
"/projects/my-project/files/app.ts",
"/projects/my-project/files/second.ts",
"/projects/my-project/files/app.ts",
"/projects/my-project/files/second.ts",
"/projects/my-project/files/second.ts",
]);
assertEquals(await readPushReceipt(projectDir), null);
assertEquals(
Expand Down
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@
"docs:coverage": "deno run --allow-read scripts/docs/docs-coverage.ts",
"docs:copy": "rm -rf ../../docs/docs/code/api-reference && cp -r docs/api-reference/ ../../docs/docs/code/api-reference/",
"docs:snippets:check": "deno run --allow-read --allow-write --allow-run --allow-env scripts/docs/check-guide-snippets.ts",
"docs:validate": "deno run --allow-read --allow-run=git scripts/docs/validate-tracked-docs.ts && deno run --allow-read scripts/docs/validate-api-reference.ts && deno run --allow-read scripts/docs/validate-guides.ts && deno run --config=scripts/test.deno.json --allow-read scripts/docs/validate-public-docs.ts && deno test --config=scripts/test.deno.json --no-check --allow-read scripts/docs/docs-coverage.test.ts && DENO_TESTING=1 deno test --no-check --allow-read --allow-env=DENO_TESTING tests/docs/guide-contracts.test.ts tests/docs/guide-content.test.ts && DENO_TESTING=1 deno test --no-check --allow-all tests/docs/guide-examples.test.ts tests/docs/guide-code-examples.test.ts && deno run -A scripts/lint/check-doc-links.ts && deno task docs:snippets:check",
"docs:validate": "deno run --allow-read --allow-run=git scripts/docs/validate-tracked-docs.ts && deno run --allow-read scripts/docs/validate-api-reference.ts && deno run --allow-read scripts/docs/validate-guides.ts && deno run --config=scripts/test.deno.json --allow-read scripts/docs/validate-public-docs.ts && deno test --config=scripts/test.deno.json --no-check --allow-read scripts/docs/docs-coverage.test.ts && deno task test:file tests/integration/docs/provider-client-examples.test.ts && DENO_TESTING=1 deno test --no-check --allow-read --allow-env=DENO_TESTING tests/docs/guide-contracts.test.ts tests/docs/guide-content.test.ts && DENO_TESTING=1 deno test --no-check --allow-all tests/docs/guide-examples.test.ts tests/docs/guide-code-examples.test.ts && deno run -A scripts/lint/check-doc-links.ts && deno task docs:snippets:check",
"docs:verify-npm": "node scripts/docs/verify-npm-exports.mjs && node scripts/docs/verify-npm-node.mjs",
"docs:check-links": "deno run -A scripts/lint/check-doc-links.ts",
"lint:ban-zod": "deno run --allow-read scripts/lint/ban-zod-imports.ts",
Expand Down
90 changes: 90 additions & 0 deletions docs/guides/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,96 @@ export default agent({
`VERYFRONT_DEFAULT_MODEL` can select another gateway default for omitted
models and `model: "auto"`. It is optional.

### Call the AI Gateway from other clients

Code outside Veryfront can use the AI Gateway through the official OpenAI and
Anthropic clients. Create a project API key with the Write permission in Studio
under **Settings > API Keys**. Inference requests are writes, so a Read-only key
is refused. The key starts with `vf_` and is bound to its project.
The examples read it from `VERYFRONT_API_KEY`, a name for your own code only.
Comment thread
kwakayama marked this conversation as resolved.
The Veryfront CLI and SDK read `VERYFRONT_API_TOKEN`, described above.

Set the project API key in your shell before running the examples:

```bash
export VERYFRONT_API_KEY="<your-project-api-key>"
```

Name the model as `<provider>/<model>`. List the models your key can use:

```bash
curl https://api.veryfront.com/ai/models \
-H "Authorization: Bearer $VERYFRONT_API_KEY"
```

Install the clients for the examples you want to run:

```bash
npm install openai @anthropic-ai/sdk
```

Use a model that supports Chat Completions with the OpenAI client at
`https://api.veryfront.com/ai/v1`. Models supporting other operations use their
corresponding endpoints. Model identifiers can include vendors this SDK has no
built-in entry for:

```ts
import OpenAI from "openai";
Comment thread
kojiwakayama marked this conversation as resolved.

const projectApiKey = process.env.VERYFRONT_API_KEY;
if (!projectApiKey) {
throw new Error("Set VERYFRONT_API_KEY before running this example.");
}

const client = new OpenAI({
baseURL: "https://api.veryfront.com/ai/v1",
apiKey: projectApiKey,
});

const completion = await client.chat.completions.create({
model: "mistral/mistral-small-2503",
messages: [{ role: "user", content: "Say hello." }],
});
```

```bash
curl https://api.veryfront.com/ai/v1/chat/completions \
-H "Authorization: Bearer $VERYFRONT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"mistral/mistral-small-2503","messages":[{"role":"user","content":"Say hello."}]}'
```

Anthropic models also speak the Anthropic Messages protocol. The Anthropic
client adds `/v1/messages` to its base URL:

```ts
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({
baseURL: "https://api.veryfront.com/ai",
apiKey: null,
authToken: process.env.VERYFRONT_API_KEY,
});

const message = await client.messages.create({
model: "anthropic/claude-sonnet-4-6",
max_tokens: 1024,
messages: [{ role: "user", content: "Say hello." }],
});
```

```bash
curl https://api.veryfront.com/ai/v1/messages \
-H "Authorization: Bearer $VERYFRONT_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{"model":"anthropic/claude-sonnet-4-6","max_tokens":1024,"messages":[{"role":"user","content":"Say hello."}]}'
```

Inside a Veryfront agent, use a `veryfront-cloud/<provider>/<model>` string
instead. The SDK routes through Veryfront's project runtime gateway for the
selected provider and model.

## Runtime conventions (recommended)

For most projects, omit `model` to use the default for your inference path. Set
Expand Down
54 changes: 54 additions & 0 deletions tests/docs/guide-code-examples.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import { parsePushArgs } from "../../cli/commands/push/command.ts";
import { parseCliArgs } from "../../cli/shared/args.ts";
import { AUTH_PRESETS } from "../../cli/scaffold/engine.ts";
import { getTemplate } from "../../templates/index.ts";
import { getVeryfrontCloudGatewayBaseUrl } from "#veryfront/provider/veryfront-cloud/shared.ts";

const EXISTING_GUIDE_EXAMPLE_SUITE = [
"agents.md",
Expand Down Expand Up @@ -206,6 +207,59 @@ describe("Guide code example coverage", () => {
});
});

describe("Guide: providers.md", () => {
const api = "https://api.veryfront.com";

async function gatewayClientSection(): Promise<string> {
const guide = await readGuide("providers.md");
const start = guide.indexOf("### Call the AI Gateway from other clients");
assert(start !== -1, "providers.md documents calling the AI Gateway from other clients");
const end = guide.indexOf("\n## ", start);
return guide.slice(start, end === -1 ? undefined : end);
}

it("shows how to export a project key for the client examples", async () => {
const section = await gatewayClientSection();

assertStringIncludes(section, 'export VERYFRONT_API_KEY="<your-project-api-key>"');
});

// The guide documents the default routes, so a host that opts back into
// vendor routes must not change what this suite compares against.
function neutralBaseUrl(provider: string): Promise<string> {
return withEnv(
{ VERYFRONT_CLOUD_GATEWAY_ROUTES: "" },
() => Promise.resolve(getVeryfrontCloudGatewayBaseUrl(api, provider)),
);
}

it("gives the OpenAI client the base URL the SDK sends OpenAI-protocol models to", async () => {
const section = await gatewayClientSection();
const base = await neutralBaseUrl("openai");

assertStringIncludes(section, `baseURL: "${base}"`);
assertStringIncludes(section, `curl ${base}/chat/completions`);
});

it("gives the Anthropic client the canonical Messages base URL", async () => {
const section = await gatewayClientSection();
const base = `${api}/ai/v1`;

// The Anthropic client appends /v1/messages to its base URL.
assertEquals(base.endsWith("/v1"), true);
assertStringIncludes(section, `baseURL: "${base.slice(0, -"/v1".length)}"`);
assertStringIncludes(section, `curl ${base}/messages`);
});

it("maps unknown SDK vendors to the OpenAI protocol and documents open provider ids", async () => {
const section = await gatewayClientSection();

assertEquals(await neutralBaseUrl("acme-labs"), `${api}/ai/v1`);
assertStringIncludes(section, "`<provider>/<model>`");
assertStringIncludes(section, `curl ${api}/ai/models`);
});
});

describe("Guide: middleware.md", () => {
it("uses the global CORS policy for preflight and the actual response", async () => {
const config = defineConfig({
Expand Down
46 changes: 46 additions & 0 deletions tests/integration/docs/provider-client-examples.deno.lock

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

Loading
Loading