Skip to content

Commit 6aa7bcb

Browse files
docs: add SDK examples for TypeScript client (#32)
* docs: add SDK examples for generateText, generateObject, and streamText Add three TypeScript examples demonstrating the @patternzones/koine-sdk: - hello.ts: basic text generation - extract-recipe.ts: structured data extraction with Zod schemas - stream-haiku.ts: real-time streaming output Includes README with prerequisites and usage instructions. * docs(examples): improve error handling and streaming visibility - Add descriptive error messages for common failures: - Connection refused (gateway not running) - 401 errors (invalid API key) - Validation errors (schema mismatch) - Stream errors - Rename stream-haiku.ts to stream.ts with longer prompt - Add chunk counter and visual dots to demonstrate streaming - Import KoineError for typed error handling * docs(examples): reorganize for multi-SDK support - Move TypeScript examples to typescript/ subdirectory - Add conversation.ts for multi-turn session persistence demo - Update README with table format and Python placeholder - Prepare structure for future Python SDK examples * docs: add examples references across documentation - CONTRIBUTING.md: add "Running Examples" section - SDK README: add Examples section with runnable commands - sdk-guide.md: add "Runnable Examples" section at end - docs/README.md: add Examples to documentation table * ci: auto-format release-please PRs with biome Add format-release-pr job that runs after release-please creates/updates a PR. This fixes the JSON formatting mismatch where release-please expands arrays to multiple lines but biome wants them on single lines. * chore: format package.json files with biome * chore: remove unused Recipe type alias * ci: dynamically get release PR branch name
1 parent 61a1fee commit 6aa7bcb

File tree

12 files changed

+410
-24
lines changed

12 files changed

+410
-24
lines changed

.github/workflows/release.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ jobs:
1717
outputs:
1818
release_created: ${{ steps.release.outputs.release_created }}
1919
tag_name: ${{ steps.release.outputs.tag_name }}
20+
pr: ${{ steps.release.outputs.pr }}
2021
steps:
2122
- name: Run release-please
2223
uses: googleapis/release-please-action@v4
@@ -25,6 +26,47 @@ jobs:
2526
config-file: release-please-config.json
2627
manifest-file: .release-please-manifest.json
2728

29+
# Format release PR to fix release-please's JSON formatting
30+
format-release-pr:
31+
needs: release-please
32+
if: ${{ needs.release-please.outputs.pr }}
33+
runs-on: ubuntu-latest
34+
timeout-minutes: 5
35+
steps:
36+
- name: Get PR branch name
37+
id: pr-branch
38+
env:
39+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40+
run: |
41+
branch=$(gh pr view "${{ needs.release-please.outputs.pr }}" --repo "${{ github.repository }}" --json headRefName -q '.headRefName')
42+
echo "branch=$branch" >> "$GITHUB_OUTPUT"
43+
44+
- name: Checkout PR branch
45+
uses: actions/checkout@v4
46+
with:
47+
ref: ${{ steps.pr-branch.outputs.branch }}
48+
token: ${{ secrets.GITHUB_TOKEN }}
49+
50+
- name: Setup Bun
51+
uses: oven-sh/setup-bun@v2
52+
with:
53+
bun-version: "1.3.3"
54+
55+
- name: Format with Biome
56+
run: bun biome format --write .
57+
58+
- name: Commit formatting changes
59+
run: |
60+
git config user.name "github-actions[bot]"
61+
git config user.email "github-actions[bot]@users.noreply.github.com"
62+
git add -A
63+
if git diff --staged --quiet; then
64+
echo "No formatting changes needed"
65+
else
66+
git commit -m "chore: format release PR with biome"
67+
git push
68+
fi
69+
2870
ci-checks:
2971
needs: release-please
3072
if: ${{ needs.release-please.outputs.release_created == 'true' }}

CONTRIBUTING.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,17 @@ bun run openapi:generate # Regenerate OpenAPI spec
1313
bun run openapi:check # Verify spec is up to date
1414
```
1515

16+
## Running Examples
17+
18+
With a gateway running, you can test SDK examples:
19+
20+
```bash
21+
bun run docs/examples/typescript/hello.ts
22+
bun run docs/examples/typescript/conversation.ts
23+
```
24+
25+
See [docs/examples/](docs/examples/) for all available examples.
26+
1627
## Pre-commit Hooks
1728

1829
Husky runs automatic checks on every commit. The following tools are strongly recommended for local development:

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
|-------|-------------|
55
| [API Reference](api-reference.md) | REST endpoints |
66
| [SDK Guide](sdk-guide.md) | TypeScript SDK |
7+
| [Examples](examples/) | Runnable SDK examples |
78
| [Docker Deployment](docker-deployment.md) | Production deployment |
89
| [Skills & Commands](skills-and-commands.md) | Extending Claude Code |
910
| [Environment Variables](environment-variables.md) | Configuration |

docs/examples/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# SDK Examples
2+
3+
Examples demonstrating how to use Koine SDKs.
4+
5+
## Prerequisites
6+
7+
1. **Docker gateway running**: Start the gateway with:
8+
```bash
9+
docker run -d --env-file .env -p 3100:3100 ghcr.io/pattern-zones-co/koine:latest
10+
```
11+
12+
2. **Environment configured**: Ensure your `.env` file has:
13+
```
14+
CLAUDE_CODE_GATEWAY_API_KEY=your-api-key
15+
GATEWAY_PORT=3100 # optional, defaults to 3100
16+
```
17+
18+
## TypeScript Examples
19+
20+
Using `@patternzones/koine-sdk`. Run from the project root:
21+
22+
```bash
23+
bun run docs/examples/typescript/hello.ts
24+
bun run docs/examples/typescript/extract-recipe.ts
25+
bun run docs/examples/typescript/stream.ts
26+
bun run docs/examples/typescript/conversation.ts
27+
```
28+
29+
| Example | Description |
30+
|---------|-------------|
31+
| `hello.ts` | Basic `generateText` usage |
32+
| `extract-recipe.ts` | Structured output with Zod schemas |
33+
| `stream.ts` | Real-time streaming with `streamText` |
34+
| `conversation.ts` | Multi-turn conversations with `sessionId` |
35+
36+
## Python Examples
37+
38+
Coming soon.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* conversation.ts - Multi-turn conversation with session persistence
3+
*
4+
* Demonstrates how to maintain context across multiple requests using sessionId.
5+
* The model remembers information from previous turns in the conversation.
6+
*
7+
* Run from project root:
8+
* bun run docs/examples/typescript/conversation.ts
9+
*/
10+
11+
import {
12+
type KoineConfig,
13+
KoineError,
14+
generateText,
15+
} from "@patternzones/koine-sdk";
16+
17+
// Bun automatically loads .env from current working directory
18+
const authKey = process.env.CLAUDE_CODE_GATEWAY_API_KEY;
19+
if (!authKey) {
20+
throw new Error("CLAUDE_CODE_GATEWAY_API_KEY is required in .env");
21+
}
22+
23+
const config: KoineConfig = {
24+
baseUrl: `http://localhost:${process.env.GATEWAY_PORT || "3100"}`,
25+
authKey,
26+
timeout: 300000,
27+
};
28+
29+
async function main() {
30+
console.log("=== Multi-turn Conversation Example ===\n");
31+
32+
// Turn 1: Introduce ourselves
33+
console.log("Turn 1: Introducing myself...");
34+
const turn1 = await generateText(config, {
35+
prompt:
36+
"My name is Alice and my favorite color is blue. Please acknowledge this.",
37+
});
38+
console.log(`Assistant: ${turn1.text}\n`);
39+
40+
// Turn 2: Ask a follow-up question using the same session
41+
console.log("Turn 2: Testing if the model remembers...");
42+
const turn2 = await generateText(config, {
43+
prompt: "What's my name and what's my favorite color?",
44+
sessionId: turn1.sessionId, // Continue the conversation
45+
});
46+
console.log(`Assistant: ${turn2.text}\n`);
47+
48+
// Turn 3: Add more context and ask another question
49+
console.log("Turn 3: Adding more context...");
50+
const turn3 = await generateText(config, {
51+
prompt:
52+
"I also have a cat named Whiskers. Now tell me everything you know about me.",
53+
sessionId: turn1.sessionId, // Same session continues
54+
});
55+
console.log(`Assistant: ${turn3.text}\n`);
56+
57+
console.log("---");
58+
console.log(`Session ID: ${turn1.sessionId}`);
59+
console.log(
60+
`Total tokens: ${turn1.usage.totalTokens + turn2.usage.totalTokens + turn3.usage.totalTokens}`,
61+
);
62+
}
63+
64+
main().catch((error) => {
65+
if (error instanceof KoineError) {
66+
console.error(`\nKoine Error [${error.code}]: ${error.message}`);
67+
if (error.code === "HTTP_ERROR" && error.message.includes("401")) {
68+
console.error(" → Check that CLAUDE_CODE_GATEWAY_API_KEY is correct");
69+
}
70+
} else if (error?.cause?.code === "ECONNREFUSED") {
71+
console.error("\nConnection refused. Is the gateway running?");
72+
console.error(
73+
" → Start it with: docker run -d --env-file .env -p 3100:3100 ghcr.io/pattern-zones-co/koine:latest",
74+
);
75+
} else {
76+
console.error("\nUnexpected error:", error);
77+
}
78+
process.exit(1);
79+
});
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* extract-recipe.ts - generateObject example with Zod schema
3+
*
4+
* Demonstrates structured data extraction using Zod schemas for type-safe output.
5+
*
6+
* Run from project root:
7+
* bun run docs/examples/typescript/extract-recipe.ts
8+
*/
9+
10+
import {
11+
type KoineConfig,
12+
KoineError,
13+
generateObject,
14+
} from "@patternzones/koine-sdk";
15+
import { z } from "zod";
16+
17+
// Bun automatically loads .env from current working directory
18+
const authKey = process.env.CLAUDE_CODE_GATEWAY_API_KEY;
19+
if (!authKey) {
20+
throw new Error("CLAUDE_CODE_GATEWAY_API_KEY is required in .env");
21+
}
22+
23+
const config: KoineConfig = {
24+
baseUrl: `http://localhost:${process.env.GATEWAY_PORT || "3100"}`,
25+
authKey,
26+
timeout: 300000,
27+
};
28+
29+
// Define the schema for a recipe
30+
const RecipeSchema = z.object({
31+
name: z.string().describe("Name of the recipe"),
32+
ingredients: z.array(z.string()).describe("List of ingredients"),
33+
steps: z.array(z.string()).describe("Cooking instructions"),
34+
prepTime: z.number().describe("Preparation time in minutes"),
35+
cookTime: z.number().describe("Cooking time in minutes"),
36+
});
37+
38+
async function main() {
39+
console.log("Extracting recipe from natural language...\n");
40+
41+
const result = await generateObject(config, {
42+
prompt: `Extract the recipe from this description:
43+
44+
Make classic pancakes by mixing 1 cup flour, 1 egg, 1 cup milk, and 2 tbsp melted butter.
45+
First combine the dry ingredients, then whisk in the wet ingredients until smooth.
46+
Heat a griddle and pour 1/4 cup batter per pancake. Cook until bubbles form, flip,
47+
and cook until golden. Takes about 5 minutes to prep and 15 minutes to cook.`,
48+
schema: RecipeSchema,
49+
});
50+
51+
console.log("Recipe extracted:");
52+
console.log(JSON.stringify(result.object, null, 2));
53+
console.log(
54+
`\nTokens used: ${result.usage.totalTokens} (input: ${result.usage.inputTokens}, output: ${result.usage.outputTokens})`,
55+
);
56+
}
57+
58+
main().catch((error) => {
59+
if (error instanceof KoineError) {
60+
console.error(`\nKoine Error [${error.code}]: ${error.message}`);
61+
if (error.code === "VALIDATION_ERROR") {
62+
console.error(" → The response didn't match the expected schema");
63+
if (error.rawText) {
64+
console.error(` → Raw response: ${error.rawText}`);
65+
}
66+
} else if (error.code === "HTTP_ERROR" && error.message.includes("401")) {
67+
console.error(" → Check that CLAUDE_CODE_GATEWAY_API_KEY is correct");
68+
}
69+
} else if (error?.cause?.code === "ECONNREFUSED") {
70+
console.error("\nConnection refused. Is the gateway running?");
71+
console.error(
72+
" → Start it with: docker run -d --env-file .env -p 3100:3100 ghcr.io/pattern-zones-co/koine:latest",
73+
);
74+
} else {
75+
console.error("\nUnexpected error:", error);
76+
}
77+
process.exit(1);
78+
});

docs/examples/typescript/hello.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* hello.ts - Basic generateText example
3+
*
4+
* Demonstrates the simplest use case: asking a question and getting a text response.
5+
*
6+
* Run from project root:
7+
* bun run docs/examples/typescript/hello.ts
8+
*/
9+
10+
import {
11+
type KoineConfig,
12+
KoineError,
13+
generateText,
14+
} from "@patternzones/koine-sdk";
15+
16+
// Bun automatically loads .env from current working directory
17+
const authKey = process.env.CLAUDE_CODE_GATEWAY_API_KEY;
18+
if (!authKey) {
19+
throw new Error("CLAUDE_CODE_GATEWAY_API_KEY is required in .env");
20+
}
21+
22+
const config: KoineConfig = {
23+
baseUrl: `http://localhost:${process.env.GATEWAY_PORT || "3100"}`,
24+
authKey,
25+
timeout: 300000,
26+
};
27+
28+
async function main() {
29+
console.log("Sending request to Koine gateway...\n");
30+
31+
const result = await generateText(config, {
32+
prompt: "What are the three primary colors? Answer in one sentence.",
33+
});
34+
35+
console.log(`Response: ${result.text}`);
36+
console.log(
37+
`\nTokens used: ${result.usage.totalTokens} (input: ${result.usage.inputTokens}, output: ${result.usage.outputTokens})`,
38+
);
39+
console.log(`Session ID: ${result.sessionId}`);
40+
}
41+
42+
main().catch((error) => {
43+
if (error instanceof KoineError) {
44+
console.error(`\nKoine Error [${error.code}]: ${error.message}`);
45+
if (error.code === "HTTP_ERROR" && error.message.includes("401")) {
46+
console.error(" → Check that CLAUDE_CODE_GATEWAY_API_KEY is correct");
47+
}
48+
} else if (error?.cause?.code === "ECONNREFUSED") {
49+
console.error("\nConnection refused. Is the gateway running?");
50+
console.error(
51+
" → Start it with: docker run -d --env-file .env -p 3100:3100 ghcr.io/pattern-zones-co/koine:latest",
52+
);
53+
} else {
54+
console.error("\nUnexpected error:", error);
55+
}
56+
process.exit(1);
57+
});

0 commit comments

Comments
 (0)