From 6f9ca7b19112d675b2fcc42fd8e074e1d5a502e6 Mon Sep 17 00:00:00 2001 From: Govind Kamtamneni Date: Fri, 6 Feb 2026 05:25:22 -0800 Subject: [PATCH 1/8] Add Copilot SDK integration guide Add documentation showing how to use GitHub Copilot SDK with Foundry Local for agentic workflows (tool calling, planning, multi-turn) via BYOK configuration. Update docs/README.md with Integration Guides section. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- docs/README.md | 6 +- docs/copilot-sdk-integration.md | 106 ++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 docs/copilot-sdk-integration.md diff --git a/docs/README.md b/docs/README.md index ee8f8469..69ed1392 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,4 +4,8 @@ The Foundry Local documentation is provided on [Microsoft Learn](https://learn.m ## API Reference -- [Foundry Local C# SDK API Reference](./cs-api/Microsoft.AI.Foundry.Local.md) \ No newline at end of file +- [Foundry Local C# SDK API Reference](./cs-api/Microsoft.AI.Foundry.Local.md) + +## Integration Guides + +- [Using GitHub Copilot SDK with Foundry Local](./copilot-sdk-integration.md) \ No newline at end of file diff --git a/docs/copilot-sdk-integration.md b/docs/copilot-sdk-integration.md new file mode 100644 index 00000000..271117a2 --- /dev/null +++ b/docs/copilot-sdk-integration.md @@ -0,0 +1,106 @@ +# Using GitHub Copilot SDK with Foundry Local + +## Overview + +For **agentic workflows** — tool calling, multi-step planning, and multi-turn conversations — you can use [GitHub Copilot SDK](https://github.com/github/copilot-sdk) with Foundry Local as the on-device inference backend. Copilot SDK provides the agentic orchestration layer while Foundry Local handles local model execution. + +This approach requires **no changes** to Foundry Local or its APIs. Copilot SDK connects to Foundry Local's OpenAI-compatible endpoint via its [Bring Your Own Key (BYOK)](https://github.com/github/copilot-sdk/blob/main/docs/auth/byok.md) feature. + +## Architecture + +``` +Your Application + ↓ +GitHub Copilot SDK (agentic orchestration: tools, planning, multi-turn) + ↓ BYOK config: type "openai" +Foundry Local (on-device inference server) + ↓ POST /v1/chat/completions (OpenAI-compatible) +Local Model (e.g., phi-3.5-mini via ONNX Runtime) +``` + +## Quick Start + +### Prerequisites + +1. **Install Foundry Local** + - Windows: `winget install Microsoft.FoundryLocal` + - macOS: `brew install microsoft/foundrylocal/foundrylocal` + +2. **Download a model** + ```bash + foundry model run phi-3.5-mini + ``` + +3. **Install GitHub Copilot SDK** — see [Copilot SDK docs](https://github.com/github/copilot-sdk) + +### Node.js / TypeScript + +```typescript +import { FoundryLocalManager } from "foundry-local-sdk"; + +// Step 1: Bootstrap Foundry Local (starts service + loads model) +const manager = new FoundryLocalManager(); +const modelInfo = await manager.init("phi-3.5-mini"); +console.log("Endpoint:", manager.endpoint); +console.log("Model:", modelInfo.id); + +// Step 2: Configure Copilot SDK to use Foundry Local as inference backend +// Use BYOK with type "openai" — Foundry Local's API is OpenAI-compatible +const providerConfig = { + type: "openai", + baseUrl: manager.endpoint, // e.g., "http://localhost:5272/v1" + apiKey: manager.apiKey, + model: modelInfo.id, +}; + +// Step 3: Use providerConfig when creating a Copilot SDK session +// See Copilot SDK docs for full session API usage +``` + +### Python + +```python +from foundry_local import FoundryLocalManager + +# Step 1: Bootstrap Foundry Local (starts service + loads model) +manager = FoundryLocalManager("phi-3.5-mini") +model_info = manager.get_model_info("phi-3.5-mini") +print(f"Endpoint: {manager.endpoint}") +print(f"Model: {model_info.id}") + +# Step 2: Configure Copilot SDK to use Foundry Local +provider_config = { + "type": "openai", + "base_url": manager.endpoint, # e.g., "http://localhost:5272/v1" + "api_key": manager.api_key, + "model": model_info.id, +} + +# Step 3: Use provider_config when creating a Copilot SDK session +# See Copilot SDK docs for full session API usage +``` + +## When to Use Which Approach + +| Scenario | Recommended Approach | +|----------|---------------------| +| Simple chat completions | Foundry Local SDK + OpenAI client ([existing samples](../samples/)) | +| **Agentic workflows** (tools, planning, multi-turn) | **Copilot SDK + Foundry Local** (this guide) | +| Model management only (download, load, unload) | Foundry Local SDK directly | +| Production cloud inference with agentic features | Copilot SDK with cloud providers | + +> **Note:** The existing Foundry Local SDKs (Python, JavaScript, C#, Rust) remain fully supported. This guide provides an additional option for developers who need agentic orchestration capabilities. + +## Limitations + +- **Tool calling**: Depends on model support. Not all Foundry Local models support function calling. Check model capabilities with `foundry model ls`. +- **Preview APIs**: Both Foundry Local's REST API and Copilot SDK may have breaking changes during preview. +- **Model size**: On-device models are smaller than cloud models. Agentic performance (multi-step planning, complex tool use) may vary compared to cloud-hosted models. +- **Platform**: Foundry Local supports Windows (x64/arm64) and macOS (Apple Silicon). + +## Related Links + +- [GitHub Copilot SDK](https://github.com/github/copilot-sdk) — Multi-platform SDK for agentic workflows +- [Copilot SDK BYOK Documentation](https://github.com/github/copilot-sdk/blob/main/docs/auth/byok.md) — Full BYOK configuration reference +- [Foundry Local Samples](../samples/) — Existing samples using Foundry Local SDK + OpenAI client +- [Foundry Local Documentation (Microsoft Learn)](https://learn.microsoft.com/azure/ai-foundry/foundry-local/) From 90debc3ee91e742bef5827854d5d66b64d9c8462 Mon Sep 17 00:00:00 2001 From: Govind Kamtamneni Date: Fri, 6 Feb 2026 05:25:30 -0800 Subject: [PATCH 2/8] Add Copilot SDK + Foundry Local Node.js sample Working Node.js sample demonstrating the BYOK pattern: Foundry Local SDK for service/model lifecycle, OpenAI client for inference. Includes package.json, README with setup instructions, and streaming chat completion example. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- .../js/copilot-sdk-foundry-local/README.md | 38 +++++++++++++ .../js/copilot-sdk-foundry-local/package.json | 14 +++++ .../js/copilot-sdk-foundry-local/src/app.js | 54 +++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 samples/js/copilot-sdk-foundry-local/README.md create mode 100644 samples/js/copilot-sdk-foundry-local/package.json create mode 100644 samples/js/copilot-sdk-foundry-local/src/app.js diff --git a/samples/js/copilot-sdk-foundry-local/README.md b/samples/js/copilot-sdk-foundry-local/README.md new file mode 100644 index 00000000..633cef23 --- /dev/null +++ b/samples/js/copilot-sdk-foundry-local/README.md @@ -0,0 +1,38 @@ +# Copilot SDK + Foundry Local Sample + +This sample demonstrates using [GitHub Copilot SDK](https://github.com/github/copilot-sdk) with [Foundry Local](https://github.com/microsoft/Foundry-Local) for on-device agentic AI workflows. + +## What This Shows + +- Bootstrapping Foundry Local programmatically using the Foundry Local SDK +- Configuring the provider connection for an OpenAI-compatible local endpoint +- Sending a chat completion request to a locally-running model + +## Prerequisites + +1. [Foundry Local](https://github.com/microsoft/Foundry-Local#installing) installed +2. Node.js 18+ + +## Setup and Run + +```bash +cd samples/js/copilot-sdk-foundry-local +npm install +npm start +``` + +The sample will: +1. Start the Foundry Local service (if not already running) +2. Download and load the `phi-3.5-mini` model (if not cached) +3. Send a chat completion request to the local model via the OpenAI-compatible API +4. Print the streaming response + +## How It Works + +Foundry Local provides the on-device inference server with an OpenAI-compatible API. The sample uses the Foundry Local SDK for service/model lifecycle and the OpenAI client for inference — the same pattern Copilot SDK uses internally via BYOK: + +``` +App → Foundry Local SDK (bootstrap) → OpenAI client → Foundry Local server → Local model +``` + +For full agentic capabilities (tool calling, planning, multi-turn), see the [Copilot SDK integration guide](../../../docs/copilot-sdk-integration.md). diff --git a/samples/js/copilot-sdk-foundry-local/package.json b/samples/js/copilot-sdk-foundry-local/package.json new file mode 100644 index 00000000..ce594286 --- /dev/null +++ b/samples/js/copilot-sdk-foundry-local/package.json @@ -0,0 +1,14 @@ +{ + "name": "copilot-sdk-foundry-local-sample", + "version": "1.0.0", + "description": "Sample: Using GitHub Copilot SDK with Foundry Local for agentic workflows", + "type": "module", + "main": "src/app.js", + "scripts": { + "start": "node src/app.js" + }, + "dependencies": { + "foundry-local-sdk": "latest", + "openai": "^4.0.0" + } +} diff --git a/samples/js/copilot-sdk-foundry-local/src/app.js b/samples/js/copilot-sdk-foundry-local/src/app.js new file mode 100644 index 00000000..2adb6509 --- /dev/null +++ b/samples/js/copilot-sdk-foundry-local/src/app.js @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { FoundryLocalManager } from "foundry-local-sdk"; +import { OpenAI } from "openai"; + +const alias = "phi-3.5-mini"; + +async function main() { + // Step 1: Bootstrap Foundry Local + // This starts the service if not running and downloads/loads the model + console.log("Initializing Foundry Local..."); + const manager = new FoundryLocalManager(); + const modelInfo = await manager.init(alias); + console.log(`Model: ${modelInfo.id}`); + console.log(`Endpoint: ${manager.endpoint}`); + console.log(""); + + // Step 2: Create an OpenAI-compatible client pointing to Foundry Local + // This is the same pattern Copilot SDK uses internally with BYOK type "openai" + const client = new OpenAI({ + baseURL: manager.endpoint, + apiKey: manager.apiKey, + }); + + // Step 3: Send a chat completion request + console.log("Sending prompt to local model...\n"); + const stream = await client.chat.completions.create({ + model: modelInfo.id, + messages: [ + { + role: "system", + content: "You are a helpful assistant running locally via Foundry Local.", + }, + { + role: "user", + content: "Explain the golden ratio in one paragraph.", + }, + ], + stream: true, + }); + + // Step 4: Stream the response + process.stdout.write("Assistant: "); + for await (const chunk of stream) { + const content = chunk.choices[0]?.delta?.content; + if (content) { + process.stdout.write(content); + } + } + console.log("\n\nDone!"); +} + +main().catch(console.error); From 26c96a5a6d042b51ad661d3cab917c505090407e Mon Sep 17 00:00:00 2001 From: Govind Kamtamneni Date: Fri, 6 Feb 2026 09:41:19 -0800 Subject: [PATCH 3/8] Rewrite sample to use real Copilot SDK with BYOK pointing to Foundry Local Replace the OpenAI client wrapper with actual @github/copilot-sdk usage: - Use CopilotClient, createSession, defineTool from @github/copilot-sdk - Configure BYOK provider with type 'openai' pointing to Foundry Local - Add custom tool (get_system_info) demonstrating agentic capabilities - Add streaming event handling and multi-turn conversation - Switch from JS to TypeScript (matching Copilot SDK cookbook pattern) - Add Copilot CLI as prerequisite in README and integration guide - Rewrite integration guide with real API examples (Node.js + Python) --- docs/copilot-sdk-integration.md | 241 +++++++++++++++--- .../js/copilot-sdk-foundry-local/README.md | 75 +++++- .../js/copilot-sdk-foundry-local/package.json | 11 +- .../js/copilot-sdk-foundry-local/src/app.js | 54 ---- .../js/copilot-sdk-foundry-local/src/app.ts | 108 ++++++++ 5 files changed, 378 insertions(+), 111 deletions(-) delete mode 100644 samples/js/copilot-sdk-foundry-local/src/app.js create mode 100644 samples/js/copilot-sdk-foundry-local/src/app.ts diff --git a/docs/copilot-sdk-integration.md b/docs/copilot-sdk-integration.md index 271117a2..d78817e1 100644 --- a/docs/copilot-sdk-integration.md +++ b/docs/copilot-sdk-integration.md @@ -10,76 +10,233 @@ This approach requires **no changes** to Foundry Local or its APIs. Copilot SDK ``` Your Application - ↓ -GitHub Copilot SDK (agentic orchestration: tools, planning, multi-turn) - ↓ BYOK config: type "openai" -Foundry Local (on-device inference server) - ↓ POST /v1/chat/completions (OpenAI-compatible) -Local Model (e.g., phi-3.5-mini via ONNX Runtime) + | + ├─ foundry-local-sdk ──→ Foundry Local service (model lifecycle) + | + └─ @github/copilot-sdk (CopilotClient) + | + ├─ JSON-RPC ──→ Copilot CLI (agent orchestration, tool execution) + | + └─ BYOK provider: { type: "openai", baseUrl: "http://localhost:5272/v1" } + | + └─ POST /v1/chat/completions ──→ Foundry Local (on-device inference) + | + └─ Local Model (e.g., phi-3.5-mini via ONNX Runtime) ``` -## Quick Start +**Key components:** -### Prerequisites +- **Foundry Local SDK** (`foundry-local-sdk`) — Manages the local inference service lifecycle (start, model download/load) +- **Copilot SDK** (`@github/copilot-sdk`) — Provides `CopilotClient` for agentic orchestration (sessions, tools, streaming, multi-turn) +- **Copilot CLI** — Background process that the SDK communicates with over JSON-RPC. Handles agent orchestration and tool execution +- **BYOK** — Routes inference requests from Copilot SDK to Foundry Local's OpenAI-compatible endpoint instead of GitHub Copilot's cloud + +## Prerequisites 1. **Install Foundry Local** - Windows: `winget install Microsoft.FoundryLocal` - macOS: `brew install microsoft/foundrylocal/foundrylocal` -2. **Download a model** +2. **Install GitHub Copilot CLI** and authenticate + - See [Copilot CLI installation guide](https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli) + - Verify: `copilot --version` + +3. **Download a model** ```bash foundry model run phi-3.5-mini ``` -3. **Install GitHub Copilot SDK** — see [Copilot SDK docs](https://github.com/github/copilot-sdk) +## Quick Start: Node.js / TypeScript + +```typescript +import { CopilotClient, defineTool } from "@github/copilot-sdk"; +import { FoundryLocalManager } from "foundry-local-sdk"; + +// Bootstrap Foundry Local (starts service + loads model) +const manager = new FoundryLocalManager(); +const modelInfo = await manager.init("phi-3.5-mini"); + +// Create a Copilot SDK client (communicates with Copilot CLI over JSON-RPC) +const client = new CopilotClient(); + +// Create a session with BYOK pointing to Foundry Local +const session = await client.createSession({ + model: modelInfo.id, + provider: { + type: "openai", + baseUrl: manager.endpoint, // e.g., "http://localhost:5272/v1" + apiKey: manager.apiKey, + wireApi: "completions", // Foundry Local uses Chat Completions API + }, + streaming: true, +}); + +// Subscribe to streaming response chunks +session.on("assistant.message_delta", (event) => { + process.stdout.write(event.data.deltaContent); +}); + +// Send a message and wait for the complete response +await session.sendAndWait({ prompt: "What is the golden ratio?" }); + +// Clean up +await session.destroy(); +await client.stop(); +``` + +Install and run: + +```bash +npm install @github/copilot-sdk foundry-local-sdk tsx +npx tsx app.ts +``` + +## Quick Start: Python + +```python +import asyncio +import sys +from copilot import CopilotClient +from copilot.generated.session_events import SessionEventType +from foundry_local import FoundryLocalManager + +async def main(): + # Bootstrap Foundry Local + manager = FoundryLocalManager("phi-3.5-mini") + model_info = manager.get_model_info("phi-3.5-mini") + + # Create a Copilot SDK client + client = CopilotClient() + await client.start() + + # Create a session with BYOK pointing to Foundry Local + session = await client.create_session({ + "model": model_info.id, + "provider": { + "type": "openai", + "base_url": manager.endpoint, + "api_key": manager.api_key, + "wire_api": "completions", + }, + "streaming": True, + }) + + # Subscribe to streaming response chunks + def on_event(event): + if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA: + sys.stdout.write(event.data.delta_content) + sys.stdout.flush() + + session.on(on_event) + + await session.send_and_wait({"prompt": "What is the golden ratio?"}) + + await session.destroy() + await client.stop() + +asyncio.run(main()) +``` + +Install and run: + +```bash +pip install github-copilot-sdk foundry-local-sdk +python app.py +``` + +## Adding Custom Tools + +Copilot SDK supports custom tools that the model can invoke during a conversation. This enables agentic workflows where the model can call your code: ### Node.js / TypeScript ```typescript +import { CopilotClient, defineTool } from "@github/copilot-sdk"; import { FoundryLocalManager } from "foundry-local-sdk"; -// Step 1: Bootstrap Foundry Local (starts service + loads model) const manager = new FoundryLocalManager(); const modelInfo = await manager.init("phi-3.5-mini"); -console.log("Endpoint:", manager.endpoint); -console.log("Model:", modelInfo.id); - -// Step 2: Configure Copilot SDK to use Foundry Local as inference backend -// Use BYOK with type "openai" — Foundry Local's API is OpenAI-compatible -const providerConfig = { - type: "openai", - baseUrl: manager.endpoint, // e.g., "http://localhost:5272/v1" - apiKey: manager.apiKey, + +// Define a tool the model can call +const getSystemInfo = defineTool("get_system_info", { + description: "Get information about the local AI system", + parameters: { + type: "object", + properties: { + query: { type: "string", description: "What to look up: 'model' or 'endpoint'" }, + }, + required: ["query"], + }, + handler: async (args: { query: string }) => { + if (args.query === "model") { + return { modelId: modelInfo.id, runtime: "ONNX Runtime" }; + } + return { url: manager.endpoint, protocol: "OpenAI-compatible" }; + }, +}); + +const client = new CopilotClient(); +const session = await client.createSession({ model: modelInfo.id, -}; + provider: { + type: "openai", + baseUrl: manager.endpoint, + apiKey: manager.apiKey, + wireApi: "completions", + }, + streaming: true, + tools: [getSystemInfo], +}); -// Step 3: Use providerConfig when creating a Copilot SDK session -// See Copilot SDK docs for full session API usage +session.on("assistant.message_delta", (event) => { + process.stdout.write(event.data.deltaContent); +}); + +await session.sendAndWait({ + prompt: "What model am I running locally? Use the get_system_info tool to find out.", +}); + +await session.destroy(); +await client.stop(); ``` ### Python ```python -from foundry_local import FoundryLocalManager +from copilot import CopilotClient +from copilot.tools import define_tool +from pydantic import BaseModel, Field -# Step 1: Bootstrap Foundry Local (starts service + loads model) -manager = FoundryLocalManager("phi-3.5-mini") -model_info = manager.get_model_info("phi-3.5-mini") -print(f"Endpoint: {manager.endpoint}") -print(f"Model: {model_info.id}") - -# Step 2: Configure Copilot SDK to use Foundry Local -provider_config = { - "type": "openai", - "base_url": manager.endpoint, # e.g., "http://localhost:5272/v1" - "api_key": manager.api_key, - "model": model_info.id, -} +class SystemInfoParams(BaseModel): + query: str = Field(description="What to look up: 'model' or 'endpoint'") + +@define_tool(description="Get information about the local AI system") +async def get_system_info(params: SystemInfoParams) -> dict: + if params.query == "model": + return {"modelId": model_info.id, "runtime": "ONNX Runtime"} + return {"url": manager.endpoint, "protocol": "OpenAI-compatible"} -# Step 3: Use provider_config when creating a Copilot SDK session -# See Copilot SDK docs for full session API usage +# Pass tools when creating the session: +session = await client.create_session({ + "model": model_info.id, + "provider": { ... }, # BYOK config as above + "tools": [get_system_info], +}) ``` +## BYOK Provider Configuration Reference + +The `provider` object in `createSession()` configures where Copilot SDK sends inference requests: + +| Field | Type | Description | +|-------|------|-------------| +| `type` | `"openai"` | Provider type. Use `"openai"` for Foundry Local (OpenAI-compatible) | +| `baseUrl` | string | Foundry Local endpoint, e.g., `"http://localhost:5272/v1"` | +| `apiKey` | string | API key (optional for local endpoints) | +| `wireApi` | `"completions"` \| `"responses"` | API format. Use `"completions"` for Foundry Local | + +For the full BYOK reference including Azure, Anthropic, and other providers, see [Copilot SDK BYOK docs](https://github.com/github/copilot-sdk/blob/main/docs/auth/byok.md). + ## When to Use Which Approach | Scenario | Recommended Approach | @@ -93,14 +250,20 @@ provider_config = { ## Limitations +- **Copilot CLI required**: The Copilot SDK requires the [Copilot CLI](https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli) to be installed and authenticated. The SDK communicates with it over JSON-RPC. - **Tool calling**: Depends on model support. Not all Foundry Local models support function calling. Check model capabilities with `foundry model ls`. - **Preview APIs**: Both Foundry Local's REST API and Copilot SDK may have breaking changes during preview. - **Model size**: On-device models are smaller than cloud models. Agentic performance (multi-step planning, complex tool use) may vary compared to cloud-hosted models. - **Platform**: Foundry Local supports Windows (x64/arm64) and macOS (Apple Silicon). +## Working Sample + +See the complete working sample at [`samples/js/copilot-sdk-foundry-local/`](../samples/js/copilot-sdk-foundry-local/) which demonstrates bootstrapping, BYOK configuration, tool calling, streaming, and multi-turn conversation. + ## Related Links - [GitHub Copilot SDK](https://github.com/github/copilot-sdk) — Multi-platform SDK for agentic workflows +- [Copilot SDK Getting Started](https://github.com/github/copilot-sdk/blob/main/docs/getting-started.md) — Official tutorial - [Copilot SDK BYOK Documentation](https://github.com/github/copilot-sdk/blob/main/docs/auth/byok.md) — Full BYOK configuration reference - [Foundry Local Samples](../samples/) — Existing samples using Foundry Local SDK + OpenAI client - [Foundry Local Documentation (Microsoft Learn)](https://learn.microsoft.com/azure/ai-foundry/foundry-local/) diff --git a/samples/js/copilot-sdk-foundry-local/README.md b/samples/js/copilot-sdk-foundry-local/README.md index 633cef23..387afb22 100644 --- a/samples/js/copilot-sdk-foundry-local/README.md +++ b/samples/js/copilot-sdk-foundry-local/README.md @@ -4,14 +4,24 @@ This sample demonstrates using [GitHub Copilot SDK](https://github.com/github/co ## What This Shows -- Bootstrapping Foundry Local programmatically using the Foundry Local SDK -- Configuring the provider connection for an OpenAI-compatible local endpoint -- Sending a chat completion request to a locally-running model +- Bootstrapping Foundry Local with the Foundry Local SDK (service lifecycle + model management) +- Configuring Copilot SDK's **BYOK (Bring Your Own Key)** to use Foundry Local as the inference backend +- Creating a Copilot session with a **custom tool** (agentic capability) +- Streaming responses and multi-turn conversation via the Copilot SDK session API ## Prerequisites -1. [Foundry Local](https://github.com/microsoft/Foundry-Local#installing) installed -2. Node.js 18+ +1. **[Foundry Local](https://github.com/microsoft/Foundry-Local#installing)** installed +2. **[GitHub Copilot CLI](https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli)** installed and authenticated +3. **Node.js 18+** + +Verify prerequisites: + +```bash +foundry --version +copilot --version +node --version +``` ## Setup and Run @@ -21,18 +31,55 @@ npm install npm start ``` -The sample will: -1. Start the Foundry Local service (if not already running) -2. Download and load the `phi-3.5-mini` model (if not cached) -3. Send a chat completion request to the local model via the OpenAI-compatible API -4. Print the streaming response +## What Happens -## How It Works +1. **Foundry Local bootstrap** — Starts the local inference service (if not running) and downloads/loads the `phi-3.5-mini` model +2. **Copilot SDK client creation** — Creates a `CopilotClient` which communicates with the Copilot CLI over JSON-RPC +3. **BYOK session** — Creates a session with `provider: { type: "openai", baseUrl: "" }`, routing all inference through Foundry Local instead of GitHub Copilot's cloud +4. **Tool calling** — Defines a `get_system_info` tool that the model can invoke, demonstrating agentic capabilities +5. **Multi-turn conversation** — Sends a follow-up message in the same session -Foundry Local provides the on-device inference server with an OpenAI-compatible API. The sample uses the Foundry Local SDK for service/model lifecycle and the OpenAI client for inference — the same pattern Copilot SDK uses internally via BYOK: +## Architecture ``` -App → Foundry Local SDK (bootstrap) → OpenAI client → Foundry Local server → Local model +Your App (this sample) + | + ├─ foundry-local-sdk ──→ Foundry Local service (model lifecycle) + | + └─ @github/copilot-sdk + | + ├─ JSON-RPC ──→ Copilot CLI (agent orchestration) + | + └─ BYOK provider config + | + └─ POST /v1/chat/completions ──→ Foundry Local (inference) + | + └─ Local Model (phi-3.5-mini via ONNX Runtime) +``` + +## Key Configuration: BYOK Provider + +The critical piece is the `provider` config in `createSession()`: + +```typescript +const session = await client.createSession({ + model: modelInfo.id, + provider: { + type: "openai", // Foundry Local exposes OpenAI-compatible API + baseUrl: manager.endpoint, // e.g., "http://localhost:5272/v1" + apiKey: manager.apiKey, + wireApi: "completions", // Chat Completions API format + }, + streaming: true, + tools: [getSystemInfo], +}); ``` -For full agentic capabilities (tool calling, planning, multi-turn), see the [Copilot SDK integration guide](../../../docs/copilot-sdk-integration.md). +This tells Copilot SDK to route inference requests to Foundry Local's endpoint instead of GitHub Copilot's cloud service. See the [Copilot SDK BYOK documentation](https://github.com/github/copilot-sdk/blob/main/docs/auth/byok.md) for all provider options. + +## Related + +- [Copilot SDK Integration Guide](../../../docs/copilot-sdk-integration.md) — Full integration guide with architecture details +- [Copilot SDK Getting Started](https://github.com/github/copilot-sdk/blob/main/docs/getting-started.md) — Official Copilot SDK tutorial +- [Copilot SDK BYOK Docs](https://github.com/github/copilot-sdk/blob/main/docs/auth/byok.md) — Full BYOK configuration reference +- [Foundry Local hello-foundry-local sample](../hello-foundry-local/) — Simpler sample using OpenAI client directly (no Copilot SDK) diff --git a/samples/js/copilot-sdk-foundry-local/package.json b/samples/js/copilot-sdk-foundry-local/package.json index ce594286..15e3d803 100644 --- a/samples/js/copilot-sdk-foundry-local/package.json +++ b/samples/js/copilot-sdk-foundry-local/package.json @@ -3,12 +3,15 @@ "version": "1.0.0", "description": "Sample: Using GitHub Copilot SDK with Foundry Local for agentic workflows", "type": "module", - "main": "src/app.js", "scripts": { - "start": "node src/app.js" + "start": "npx tsx src/app.ts" }, "dependencies": { - "foundry-local-sdk": "latest", - "openai": "^4.0.0" + "@github/copilot-sdk": "latest", + "foundry-local-sdk": "latest" + }, + "devDependencies": { + "tsx": "^4.0.0", + "typescript": "^5.0.0" } } diff --git a/samples/js/copilot-sdk-foundry-local/src/app.js b/samples/js/copilot-sdk-foundry-local/src/app.js deleted file mode 100644 index 2adb6509..00000000 --- a/samples/js/copilot-sdk-foundry-local/src/app.js +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { FoundryLocalManager } from "foundry-local-sdk"; -import { OpenAI } from "openai"; - -const alias = "phi-3.5-mini"; - -async function main() { - // Step 1: Bootstrap Foundry Local - // This starts the service if not running and downloads/loads the model - console.log("Initializing Foundry Local..."); - const manager = new FoundryLocalManager(); - const modelInfo = await manager.init(alias); - console.log(`Model: ${modelInfo.id}`); - console.log(`Endpoint: ${manager.endpoint}`); - console.log(""); - - // Step 2: Create an OpenAI-compatible client pointing to Foundry Local - // This is the same pattern Copilot SDK uses internally with BYOK type "openai" - const client = new OpenAI({ - baseURL: manager.endpoint, - apiKey: manager.apiKey, - }); - - // Step 3: Send a chat completion request - console.log("Sending prompt to local model...\n"); - const stream = await client.chat.completions.create({ - model: modelInfo.id, - messages: [ - { - role: "system", - content: "You are a helpful assistant running locally via Foundry Local.", - }, - { - role: "user", - content: "Explain the golden ratio in one paragraph.", - }, - ], - stream: true, - }); - - // Step 4: Stream the response - process.stdout.write("Assistant: "); - for await (const chunk of stream) { - const content = chunk.choices[0]?.delta?.content; - if (content) { - process.stdout.write(content); - } - } - console.log("\n\nDone!"); -} - -main().catch(console.error); diff --git a/samples/js/copilot-sdk-foundry-local/src/app.ts b/samples/js/copilot-sdk-foundry-local/src/app.ts new file mode 100644 index 00000000..c2820b72 --- /dev/null +++ b/samples/js/copilot-sdk-foundry-local/src/app.ts @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { CopilotClient, defineTool } from "@github/copilot-sdk"; +import { FoundryLocalManager } from "foundry-local-sdk"; + +const alias = "phi-3.5-mini"; + +async function main() { + // Step 1: Bootstrap Foundry Local + // This starts the service if not running and downloads/loads the model + console.log("Initializing Foundry Local..."); + const manager = new FoundryLocalManager(); + const modelInfo = await manager.init(alias); + console.log(`Model: ${modelInfo.id}`); + console.log(`Endpoint: ${manager.endpoint}`); + console.log(""); + + // Step 2: Create a Copilot SDK client + // The SDK communicates with the Copilot CLI over JSON-RPC + const client = new CopilotClient(); + + // Step 3: Define a custom tool the model can call + // This demonstrates agentic capabilities beyond simple chat completions + const getSystemInfo = defineTool("get_system_info", { + description: "Get information about the local AI system", + parameters: { + type: "object", + properties: { + query: { + type: "string", + description: + "What system information to retrieve: 'model', 'endpoint', or 'capabilities'", + }, + }, + required: ["query"], + }, + handler: async (args: { query: string }) => { + switch (args.query) { + case "model": + return { + modelId: modelInfo.id, + alias, + runtime: "ONNX Runtime (Foundry Local)", + }; + case "endpoint": + return { + url: manager.endpoint, + protocol: "OpenAI-compatible", + local: true, + }; + case "capabilities": + return { + chat: true, + streaming: true, + localInference: true, + noCloudRequired: true, + }; + default: + return { error: `Unknown query: ${args.query}` }; + } + }, + }); + + // Step 4: Create a session with BYOK pointing to Foundry Local + // The provider config tells Copilot SDK to use Foundry Local's + // OpenAI-compatible endpoint instead of GitHub Copilot's cloud service + const session = await client.createSession({ + model: modelInfo.id, + provider: { + type: "openai", + baseUrl: manager.endpoint, // e.g., "http://localhost:5272/v1" + apiKey: manager.apiKey, + wireApi: "completions", // Foundry Local uses Chat Completions API + }, + streaming: true, + tools: [getSystemInfo], + }); + + // Step 5: Subscribe to streaming events + session.on("assistant.message_delta", (event) => { + process.stdout.write(event.data.deltaContent); + }); + + // Step 6: Send a prompt that exercises tool calling + console.log("Asking Copilot SDK about the local AI system...\n"); + process.stdout.write("Assistant: "); + await session.sendAndWait({ + prompt: + "What model am I running locally? Use the get_system_info tool to find out, then summarize the local AI setup.", + }); + console.log("\n"); + + // Step 7: Send a follow-up chat message (multi-turn conversation) + console.log("Sending a follow-up question...\n"); + process.stdout.write("Assistant: "); + await session.sendAndWait({ + prompt: "Explain the golden ratio in one paragraph.", + }); + console.log("\n"); + + // Step 8: Clean up + await session.destroy(); + await client.stop(); + console.log("Done!"); +} + +main().catch(console.error); From b9dec2e10675fddeb9784b82044d8ed0a5def872 Mon Sep 17 00:00:00 2001 From: Govind Kamtamneni Date: Fri, 6 Feb 2026 09:52:10 -0800 Subject: [PATCH 4/8] Update sample to use gpt-oss-20b model instead of phi-3.5-mini --- docs/copilot-sdk-integration.md | 12 ++++++------ samples/js/copilot-sdk-foundry-local/README.md | 4 ++-- samples/js/copilot-sdk-foundry-local/src/app.ts | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/copilot-sdk-integration.md b/docs/copilot-sdk-integration.md index d78817e1..aa2d8dd2 100644 --- a/docs/copilot-sdk-integration.md +++ b/docs/copilot-sdk-integration.md @@ -21,7 +21,7 @@ Your Application | └─ POST /v1/chat/completions ──→ Foundry Local (on-device inference) | - └─ Local Model (e.g., phi-3.5-mini via ONNX Runtime) + └─ Local Model (e.g., gpt-oss-20b via ONNX Runtime) ``` **Key components:** @@ -43,7 +43,7 @@ Your Application 3. **Download a model** ```bash - foundry model run phi-3.5-mini + foundry model run gpt-oss-20b ``` ## Quick Start: Node.js / TypeScript @@ -54,7 +54,7 @@ import { FoundryLocalManager } from "foundry-local-sdk"; // Bootstrap Foundry Local (starts service + loads model) const manager = new FoundryLocalManager(); -const modelInfo = await manager.init("phi-3.5-mini"); +const modelInfo = await manager.init("gpt-oss-20b"); // Create a Copilot SDK client (communicates with Copilot CLI over JSON-RPC) const client = new CopilotClient(); @@ -102,8 +102,8 @@ from foundry_local import FoundryLocalManager async def main(): # Bootstrap Foundry Local - manager = FoundryLocalManager("phi-3.5-mini") - model_info = manager.get_model_info("phi-3.5-mini") + manager = FoundryLocalManager("gpt-oss-20b") + model_info = manager.get_model_info("gpt-oss-20b") # Create a Copilot SDK client client = CopilotClient() @@ -155,7 +155,7 @@ import { CopilotClient, defineTool } from "@github/copilot-sdk"; import { FoundryLocalManager } from "foundry-local-sdk"; const manager = new FoundryLocalManager(); -const modelInfo = await manager.init("phi-3.5-mini"); +const modelInfo = await manager.init("gpt-oss-20b"); // Define a tool the model can call const getSystemInfo = defineTool("get_system_info", { diff --git a/samples/js/copilot-sdk-foundry-local/README.md b/samples/js/copilot-sdk-foundry-local/README.md index 387afb22..ac93bb29 100644 --- a/samples/js/copilot-sdk-foundry-local/README.md +++ b/samples/js/copilot-sdk-foundry-local/README.md @@ -33,7 +33,7 @@ npm start ## What Happens -1. **Foundry Local bootstrap** — Starts the local inference service (if not running) and downloads/loads the `phi-3.5-mini` model +1. **Foundry Local bootstrap** — Starts the local inference service (if not running) and downloads/loads the `gpt-oss-20b` model 2. **Copilot SDK client creation** — Creates a `CopilotClient` which communicates with the Copilot CLI over JSON-RPC 3. **BYOK session** — Creates a session with `provider: { type: "openai", baseUrl: "" }`, routing all inference through Foundry Local instead of GitHub Copilot's cloud 4. **Tool calling** — Defines a `get_system_info` tool that the model can invoke, demonstrating agentic capabilities @@ -54,7 +54,7 @@ Your App (this sample) | └─ POST /v1/chat/completions ──→ Foundry Local (inference) | - └─ Local Model (phi-3.5-mini via ONNX Runtime) + └─ Local Model (gpt-oss-20b via ONNX Runtime) ``` ## Key Configuration: BYOK Provider diff --git a/samples/js/copilot-sdk-foundry-local/src/app.ts b/samples/js/copilot-sdk-foundry-local/src/app.ts index c2820b72..f4a45a99 100644 --- a/samples/js/copilot-sdk-foundry-local/src/app.ts +++ b/samples/js/copilot-sdk-foundry-local/src/app.ts @@ -4,7 +4,7 @@ import { CopilotClient, defineTool } from "@github/copilot-sdk"; import { FoundryLocalManager } from "foundry-local-sdk"; -const alias = "phi-3.5-mini"; +const alias = "gpt-oss-20b"; async function main() { // Step 1: Bootstrap Foundry Local From 219e40e96986cdfd96a2475800c02cf52a2b7be9 Mon Sep 17 00:00:00 2001 From: Govind Kamtamneni Date: Fri, 6 Feb 2026 11:07:11 -0800 Subject: [PATCH 5/8] Switch sample from gpt-oss-20b to phi-4-mini - gpt-oss-20b outputs internal reasoning tokens that break Copilot SDK - phi-4-mini works cleanly with BYOK + streaming + multi-turn - Add zod dependency for typed tool parameters - Rewrite app.ts with custom sendMessage helper to handle Foundry Local streaming quirk (missing finish_reason) - Update integration guide and sample README --- docs/copilot-sdk-integration.md | 12 +- .../js/copilot-sdk-foundry-local/README.md | 4 +- .../js/copilot-sdk-foundry-local/package.json | 3 +- .../js/copilot-sdk-foundry-local/src/app.ts | 122 +++++++++--------- 4 files changed, 68 insertions(+), 73 deletions(-) diff --git a/docs/copilot-sdk-integration.md b/docs/copilot-sdk-integration.md index aa2d8dd2..7fba9a5e 100644 --- a/docs/copilot-sdk-integration.md +++ b/docs/copilot-sdk-integration.md @@ -21,7 +21,7 @@ Your Application | └─ POST /v1/chat/completions ──→ Foundry Local (on-device inference) | - └─ Local Model (e.g., gpt-oss-20b via ONNX Runtime) + └─ Local Model (e.g., phi-4-mini via ONNX Runtime) ``` **Key components:** @@ -43,7 +43,7 @@ Your Application 3. **Download a model** ```bash - foundry model run gpt-oss-20b + foundry model run phi-4-mini ``` ## Quick Start: Node.js / TypeScript @@ -54,7 +54,7 @@ import { FoundryLocalManager } from "foundry-local-sdk"; // Bootstrap Foundry Local (starts service + loads model) const manager = new FoundryLocalManager(); -const modelInfo = await manager.init("gpt-oss-20b"); +const modelInfo = await manager.init("phi-4-mini"); // Create a Copilot SDK client (communicates with Copilot CLI over JSON-RPC) const client = new CopilotClient(); @@ -102,8 +102,8 @@ from foundry_local import FoundryLocalManager async def main(): # Bootstrap Foundry Local - manager = FoundryLocalManager("gpt-oss-20b") - model_info = manager.get_model_info("gpt-oss-20b") + manager = FoundryLocalManager("phi-4-mini") + model_info = manager.get_model_info("phi-4-mini") # Create a Copilot SDK client client = CopilotClient() @@ -155,7 +155,7 @@ import { CopilotClient, defineTool } from "@github/copilot-sdk"; import { FoundryLocalManager } from "foundry-local-sdk"; const manager = new FoundryLocalManager(); -const modelInfo = await manager.init("gpt-oss-20b"); +const modelInfo = await manager.init("phi-4-mini"); // Define a tool the model can call const getSystemInfo = defineTool("get_system_info", { diff --git a/samples/js/copilot-sdk-foundry-local/README.md b/samples/js/copilot-sdk-foundry-local/README.md index ac93bb29..a8600c93 100644 --- a/samples/js/copilot-sdk-foundry-local/README.md +++ b/samples/js/copilot-sdk-foundry-local/README.md @@ -33,7 +33,7 @@ npm start ## What Happens -1. **Foundry Local bootstrap** — Starts the local inference service (if not running) and downloads/loads the `gpt-oss-20b` model +1. **Foundry Local bootstrap** — Starts the local inference service (if not running) and downloads/loads the `phi-4-mini` model 2. **Copilot SDK client creation** — Creates a `CopilotClient` which communicates with the Copilot CLI over JSON-RPC 3. **BYOK session** — Creates a session with `provider: { type: "openai", baseUrl: "" }`, routing all inference through Foundry Local instead of GitHub Copilot's cloud 4. **Tool calling** — Defines a `get_system_info` tool that the model can invoke, demonstrating agentic capabilities @@ -54,7 +54,7 @@ Your App (this sample) | └─ POST /v1/chat/completions ──→ Foundry Local (inference) | - └─ Local Model (gpt-oss-20b via ONNX Runtime) + └─ Local Model (phi-4-mini via ONNX Runtime) ``` ## Key Configuration: BYOK Provider diff --git a/samples/js/copilot-sdk-foundry-local/package.json b/samples/js/copilot-sdk-foundry-local/package.json index 15e3d803..cb1f1999 100644 --- a/samples/js/copilot-sdk-foundry-local/package.json +++ b/samples/js/copilot-sdk-foundry-local/package.json @@ -8,7 +8,8 @@ }, "dependencies": { "@github/copilot-sdk": "latest", - "foundry-local-sdk": "latest" + "foundry-local-sdk": "latest", + "zod": "^3.0.0" }, "devDependencies": { "tsx": "^4.0.0", diff --git a/samples/js/copilot-sdk-foundry-local/src/app.ts b/samples/js/copilot-sdk-foundry-local/src/app.ts index f4a45a99..e75935a3 100644 --- a/samples/js/copilot-sdk-foundry-local/src/app.ts +++ b/samples/js/copilot-sdk-foundry-local/src/app.ts @@ -3,103 +3,97 @@ import { CopilotClient, defineTool } from "@github/copilot-sdk"; import { FoundryLocalManager } from "foundry-local-sdk"; +import { z } from "zod"; +import * as os from "os"; -const alias = "gpt-oss-20b"; +const alias = "phi-4-mini"; + +async function sendMessage( + session: Awaited>, + prompt: string, + timeoutMs = 120_000, +) { + return new Promise((resolve) => { + let settled = false; + let turnStarted = false; + const finish = () => { + if (!settled) { + settled = true; + unsub(); + resolve(); + } + }; + + const unsub = session.on((event: any) => { + if (event.type === "assistant.turn_start") turnStarted = true; + if (turnStarted && event.type === "session.idle") finish(); + if (turnStarted && event.type === "session.error") finish(); + }); + + session.send({ prompt }).catch(() => finish()); + setTimeout(finish, timeoutMs); + }); +} async function main() { - // Step 1: Bootstrap Foundry Local - // This starts the service if not running and downloads/loads the model console.log("Initializing Foundry Local..."); const manager = new FoundryLocalManager(); const modelInfo = await manager.init(alias); console.log(`Model: ${modelInfo.id}`); - console.log(`Endpoint: ${manager.endpoint}`); - console.log(""); + console.log(`Endpoint: ${manager.endpoint}\n`); - // Step 2: Create a Copilot SDK client - // The SDK communicates with the Copilot CLI over JSON-RPC const client = new CopilotClient(); - // Step 3: Define a custom tool the model can call - // This demonstrates agentic capabilities beyond simple chat completions const getSystemInfo = defineTool("get_system_info", { - description: "Get information about the local AI system", - parameters: { - type: "object", - properties: { - query: { - type: "string", - description: - "What system information to retrieve: 'model', 'endpoint', or 'capabilities'", - }, - }, - required: ["query"], - }, - handler: async (args: { query: string }) => { - switch (args.query) { - case "model": - return { - modelId: modelInfo.id, - alias, - runtime: "ONNX Runtime (Foundry Local)", - }; - case "endpoint": - return { - url: manager.endpoint, - protocol: "OpenAI-compatible", - local: true, - }; - case "capabilities": - return { - chat: true, - streaming: true, - localInference: true, - noCloudRequired: true, - }; - default: - return { error: `Unknown query: ${args.query}` }; - } - }, + description: + "Get information about the current system including OS, architecture, memory, and CPU count", + parameters: z.object({}), + handler: async () => ({ + platform: os.platform(), + arch: os.arch(), + cpus: os.cpus().length, + totalMemory: `${Math.round(os.totalmem() / (1024 ** 3))} GB`, + freeMemory: `${Math.round(os.freemem() / (1024 ** 3))} GB`, + nodeVersion: process.version, + model: modelInfo.id, + endpoint: manager.endpoint, + }), }); - // Step 4: Create a session with BYOK pointing to Foundry Local - // The provider config tells Copilot SDK to use Foundry Local's - // OpenAI-compatible endpoint instead of GitHub Copilot's cloud service const session = await client.createSession({ model: modelInfo.id, provider: { type: "openai", - baseUrl: manager.endpoint, // e.g., "http://localhost:5272/v1" + baseUrl: manager.endpoint, apiKey: manager.apiKey, - wireApi: "completions", // Foundry Local uses Chat Completions API + wireApi: "completions", }, streaming: true, tools: [getSystemInfo], + systemMessage: { + content: + "You are a helpful AI assistant running locally via Foundry Local. " + + "You have access to tools — use them when the user asks for system or runtime information.", + }, }); - // Step 5: Subscribe to streaming events session.on("assistant.message_delta", (event) => { process.stdout.write(event.data.deltaContent); }); + session.on("tool.execution_start", (event) => { + console.log(`\n [Tool called: ${(event as any).data?.toolName ?? "unknown"}]`); + }); - // Step 6: Send a prompt that exercises tool calling - console.log("Asking Copilot SDK about the local AI system...\n"); + console.log("--- Turn 1: Ask about the local AI setup ---\n"); process.stdout.write("Assistant: "); - await session.sendAndWait({ - prompt: - "What model am I running locally? Use the get_system_info tool to find out, then summarize the local AI setup.", - }); + await sendMessage(session, "What AI model am I running locally and what are its capabilities?"); console.log("\n"); - // Step 7: Send a follow-up chat message (multi-turn conversation) - console.log("Sending a follow-up question...\n"); + console.log("--- Turn 2: Follow-up conversation ---\n"); process.stdout.write("Assistant: "); - await session.sendAndWait({ - prompt: "Explain the golden ratio in one paragraph.", - }); + await sendMessage(session, "What is the golden ratio? Explain in one paragraph."); console.log("\n"); - // Step 8: Clean up await session.destroy(); await client.stop(); console.log("Done!"); From e8c79bfe16a8a7a5236ccb5864ad2bf902afcbc3 Mon Sep 17 00:00:00 2001 From: Govind Kamtamneni Date: Fri, 6 Feb 2026 11:38:00 -0800 Subject: [PATCH 6/8] Add tool calling example with calculator, glossary lookup, and system info tools --- .../js/copilot-sdk-foundry-local/README.md | 33 ++- .../js/copilot-sdk-foundry-local/package.json | 3 +- .../src/tool-calling.ts | 208 ++++++++++++++++++ 3 files changed, 241 insertions(+), 3 deletions(-) create mode 100644 samples/js/copilot-sdk-foundry-local/src/tool-calling.ts diff --git a/samples/js/copilot-sdk-foundry-local/README.md b/samples/js/copilot-sdk-foundry-local/README.md index a8600c93..949023b6 100644 --- a/samples/js/copilot-sdk-foundry-local/README.md +++ b/samples/js/copilot-sdk-foundry-local/README.md @@ -28,16 +28,45 @@ node --version ```bash cd samples/js/copilot-sdk-foundry-local npm install +``` + +### Basic example (streaming + multi-turn) + +```bash npm start ``` +### Tool calling example (calculator, glossary lookup, system info) + +```bash +npm run tools +``` + +## Examples + +### `app.ts` — Basic (npm start) + +Bootstraps Foundry Local, creates a BYOK session, and runs a two-turn streaming conversation. + +### `tool-calling.ts` — Tool Calling (npm run tools) + +Registers three tools the model can invoke during conversation: + +| Tool | What it does | +|------|-------------| +| `calculate` | Evaluates math expressions (e.g. `Math.sqrt(144) + 8 * 3`) | +| `lookup_definition` | Looks up AI/programming terms (BYOK, ONNX, RAG, etc.) | +| `get_system_info` | Returns OS, architecture, memory, CPU count, and running model | + +Runs three turns, each designed to trigger a specific tool. When a tool is called you'll see `[Tool called: ...]` in the output. + ## What Happens 1. **Foundry Local bootstrap** — Starts the local inference service (if not running) and downloads/loads the `phi-4-mini` model 2. **Copilot SDK client creation** — Creates a `CopilotClient` which communicates with the Copilot CLI over JSON-RPC 3. **BYOK session** — Creates a session with `provider: { type: "openai", baseUrl: "" }`, routing all inference through Foundry Local instead of GitHub Copilot's cloud -4. **Tool calling** — Defines a `get_system_info` tool that the model can invoke, demonstrating agentic capabilities -5. **Multi-turn conversation** — Sends a follow-up message in the same session +4. **Tool calling** — Tools are registered at session creation; the model can invoke them and receive results mid-conversation +5. **Multi-turn conversation** — Multiple messages in the same session share conversational context ## Architecture diff --git a/samples/js/copilot-sdk-foundry-local/package.json b/samples/js/copilot-sdk-foundry-local/package.json index cb1f1999..d01a25a9 100644 --- a/samples/js/copilot-sdk-foundry-local/package.json +++ b/samples/js/copilot-sdk-foundry-local/package.json @@ -4,7 +4,8 @@ "description": "Sample: Using GitHub Copilot SDK with Foundry Local for agentic workflows", "type": "module", "scripts": { - "start": "npx tsx src/app.ts" + "start": "npx tsx src/app.ts", + "tools": "npx tsx src/tool-calling.ts" }, "dependencies": { "@github/copilot-sdk": "latest", diff --git a/samples/js/copilot-sdk-foundry-local/src/tool-calling.ts b/samples/js/copilot-sdk-foundry-local/src/tool-calling.ts new file mode 100644 index 00000000..5947a4bf --- /dev/null +++ b/samples/js/copilot-sdk-foundry-local/src/tool-calling.ts @@ -0,0 +1,208 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/** + * Tool Calling Example — Copilot SDK + Foundry Local + * + * Demonstrates multiple custom tools that the model can invoke: + * - calculate: Evaluate math expressions + * - get_system_info: Return local system details + * - lookup_definition: Look up programming term definitions + * + * Run: npm run tools + */ + +import { CopilotClient, defineTool } from "@github/copilot-sdk"; +import { FoundryLocalManager } from "foundry-local-sdk"; +import { z } from "zod"; +import * as os from "os"; + +const alias = "phi-4-mini"; + +// --------------------------------------------------------------------------- +// Helper: send a message and wait for the assistant's full reply. +// Foundry Local streaming sometimes omits finish_reason, which causes a +// session.error that can break sendAndWait(). This helper gates on +// assistant.turn_start so stale events from previous turns are ignored. +// --------------------------------------------------------------------------- +async function sendMessage( + session: Awaited>, + prompt: string, + timeoutMs = 120_000, +) { + return new Promise((resolve) => { + let settled = false; + let turnStarted = false; + const finish = () => { + if (!settled) { + settled = true; + unsub(); + resolve(); + } + }; + + const unsub = session.on((event: any) => { + if (event.type === "assistant.turn_start") turnStarted = true; + if (turnStarted && event.type === "session.idle") finish(); + if (turnStarted && event.type === "session.error") finish(); + }); + + session.send({ prompt }).catch(() => finish()); + setTimeout(finish, timeoutMs); + }); +} + +// --------------------------------------------------------------------------- +// Tool definitions +// --------------------------------------------------------------------------- + +function defineCalculateTool() { + return defineTool("calculate", { + description: + "Evaluate a math expression and return the numeric result. " + + "Supports +, -, *, /, parentheses, and Math.* functions like Math.sqrt, Math.pow.", + parameters: z.object({ + expression: z.string().describe('Math expression to evaluate, e.g. "2 + 2" or "Math.sqrt(144)"'), + }), + handler: async (args) => { + try { + // Only allow safe math characters and Math.* calls + const sanitized = args.expression.replace(/[^0-9+\-*/().,%\s]|Math\.\w+/g, (m) => + m.startsWith("Math.") ? m : "", + ); + const result = new Function(`"use strict"; return (${sanitized})`)(); + console.log(`\n → calculate("${args.expression}") = ${result}`); + return { expression: args.expression, result: Number(result) }; + } catch { + return { expression: args.expression, error: "Could not evaluate expression" }; + } + }, + }); +} + +function defineLookupTool() { + const glossary: Record = { + "byok": "Bring Your Own Key — a pattern where you supply your own API credentials to route requests to a custom endpoint instead of the default provider.", + "onnx": "Open Neural Network Exchange — an open format for representing machine learning models, enabling interoperability between frameworks.", + "rag": "Retrieval-Augmented Generation — a technique that combines a retrieval system with a generative model so responses are grounded in external documents.", + "json-rpc": "JSON Remote Procedure Call — a lightweight protocol for calling methods on a remote server using JSON-encoded messages.", + "streaming": "A technique where the server sends response tokens incrementally as they are generated, rather than waiting for the full response.", + }; + + return defineTool("lookup_definition", { + description: + "Look up the definition of a programming or AI term. " + + "Available terms: " + Object.keys(glossary).join(", "), + parameters: z.object({ + term: z.string().describe("The term to look up (case-insensitive)"), + }), + handler: async (args) => { + const key = args.term.toLowerCase().trim(); + const definition = glossary[key]; + console.log(`\n → lookup_definition("${args.term}") → ${definition ? "found" : "not found"}`); + if (definition) { + return { term: args.term, definition }; + } + return { term: args.term, error: `Term not found. Available: ${Object.keys(glossary).join(", ")}` }; + }, + }); +} + +function defineSystemInfoTool(modelId: string, endpoint: string) { + return defineTool("get_system_info", { + description: "Get information about the local system: OS, architecture, memory, CPU count, and the running model.", + parameters: z.object({}), + handler: async () => { + const info = { + platform: os.platform(), + arch: os.arch(), + cpus: os.cpus().length, + totalMemory: `${Math.round(os.totalmem() / 1024 ** 3)} GB`, + freeMemory: `${Math.round(os.freemem() / 1024 ** 3)} GB`, + nodeVersion: process.version, + model: modelId, + endpoint, + }; + console.log(`\n → get_system_info() → ${JSON.stringify(info)}`); + return info; + }, + }); +} + +// --------------------------------------------------------------------------- +// Main +// --------------------------------------------------------------------------- + +async function main() { + console.log("Initializing Foundry Local..."); + const manager = new FoundryLocalManager(); + const modelInfo = await manager.init(alias); + console.log(`Model: ${modelInfo.id}`); + console.log(`Endpoint: ${manager.endpoint}\n`); + + const calculate = defineCalculateTool(); + const lookupDefinition = defineLookupTool(); + const getSystemInfo = defineSystemInfoTool(modelInfo.id, manager.endpoint); + + const client = new CopilotClient(); + + const session = await client.createSession({ + model: modelInfo.id, + provider: { + type: "openai", + baseUrl: manager.endpoint, + apiKey: manager.apiKey, + wireApi: "completions", + }, + streaming: true, + tools: [calculate, lookupDefinition, getSystemInfo], + systemMessage: { + content: + "You are a helpful AI assistant running locally via Foundry Local. " + + "You have access to tools. ALWAYS use the appropriate tool when the user asks you to " + + "calculate something, look up a term, or get system information. " + + "Do not guess — call the tool and report its result.", + }, + }); + + // Stream assistant text to stdout + session.on("assistant.message_delta", (event) => { + process.stdout.write(event.data.deltaContent); + }); + session.on("tool.execution_start", (event) => { + console.log(`\n [Tool called: ${(event as any).data?.toolName ?? "unknown"}]`); + }); + + // --- Turn 1: Calculator tool --- + console.log("=== Turn 1: Calculator ===\n"); + process.stdout.write("User: What is the square root of 144 plus 8 times 3?\n\nAssistant: "); + await sendMessage( + session, + "Use the calculate tool to compute: Math.sqrt(144) + 8 * 3", + ); + console.log("\n"); + + // --- Turn 2: Glossary lookup tool --- + console.log("=== Turn 2: Glossary Lookup ===\n"); + process.stdout.write("User: What does BYOK mean? And what about RAG?\n\nAssistant: "); + await sendMessage( + session, + "Use the lookup_definition tool to look up 'byok' and 'rag', then explain both.", + ); + console.log("\n"); + + // --- Turn 3: System info tool --- + console.log("=== Turn 3: System Info ===\n"); + process.stdout.write("User: What system am I running on?\n\nAssistant: "); + await sendMessage( + session, + "Use the get_system_info tool to check what system this is running on, then summarize.", + ); + console.log("\n"); + + await session.destroy(); + await client.stop(); + console.log("Done!"); +} + +main().catch(console.error); From 56ee65d83d9826586cb13decbb78cc1bd53f6280 Mon Sep 17 00:00:00 2001 From: Govind Kamtamneni Date: Fri, 6 Feb 2026 11:51:31 -0800 Subject: [PATCH 7/8] Make timeout configurable via FOUNDRY_TIMEOUT_MS env var --- docs/copilot-sdk-integration.md | 7 ++++--- samples/js/copilot-sdk-foundry-local/README.md | 16 ++++++++++++++++ samples/js/copilot-sdk-foundry-local/src/app.ts | 6 +++++- .../src/tool-calling.ts | 6 +++++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/docs/copilot-sdk-integration.md b/docs/copilot-sdk-integration.md index 7fba9a5e..a12aada2 100644 --- a/docs/copilot-sdk-integration.md +++ b/docs/copilot-sdk-integration.md @@ -76,8 +76,8 @@ session.on("assistant.message_delta", (event) => { process.stdout.write(event.data.deltaContent); }); -// Send a message and wait for the complete response -await session.sendAndWait({ prompt: "What is the golden ratio?" }); +// Send a message and wait for the complete response (timeout in ms, default 60 000) +await session.sendAndWait({ prompt: "What is the golden ratio?" }, 120_000); // Clean up await session.destroy(); @@ -129,7 +129,7 @@ async def main(): session.on(on_event) - await session.send_and_wait({"prompt": "What is the golden ratio?"}) + await session.send_and_wait({"prompt": "What is the golden ratio?"}, timeout=120_000) await session.destroy() await client.stop() @@ -250,6 +250,7 @@ For the full BYOK reference including Azure, Anthropic, and other providers, see ## Limitations +- **Timeouts**: Local inference is slower than cloud. `sendAndWait()` defaults to 60 s; pass a higher value (e.g. `120_000`) for on-device models, especially on CPU-only hardware. The [working sample](../samples/js/copilot-sdk-foundry-local/) uses a `FOUNDRY_TIMEOUT_MS` environment variable for easy tuning. - **Copilot CLI required**: The Copilot SDK requires the [Copilot CLI](https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli) to be installed and authenticated. The SDK communicates with it over JSON-RPC. - **Tool calling**: Depends on model support. Not all Foundry Local models support function calling. Check model capabilities with `foundry model ls`. - **Preview APIs**: Both Foundry Local's REST API and Copilot SDK may have breaking changes during preview. diff --git a/samples/js/copilot-sdk-foundry-local/README.md b/samples/js/copilot-sdk-foundry-local/README.md index 949023b6..e911f7f7 100644 --- a/samples/js/copilot-sdk-foundry-local/README.md +++ b/samples/js/copilot-sdk-foundry-local/README.md @@ -60,6 +60,22 @@ Registers three tools the model can invoke during conversation: Runs three turns, each designed to trigger a specific tool. When a tool is called you'll see `[Tool called: ...]` in the output. +## Configuration + +### Timeout + +Both examples default to **120 seconds** per model turn. On slower hardware (CPU-only, low RAM) you may need more time. Override via the `FOUNDRY_TIMEOUT_MS` environment variable: + +```bash +# 3-minute timeout +FOUNDRY_TIMEOUT_MS=180000 npm start + +# 5-minute timeout for tool-calling (tool round-trips take longer) +FOUNDRY_TIMEOUT_MS=300000 npm run tools +``` + +The Copilot SDK's built-in `sendAndWait()` also accepts an optional `timeout` parameter (default 60 000 ms). The samples use a custom `sendMessage()` helper that wraps `session.send()` with its own timeout to work around a Foundry Local streaming quirk (missing `finish_reason`). The `FOUNDRY_TIMEOUT_MS` env var controls that helper's timeout. + ## What Happens 1. **Foundry Local bootstrap** — Starts the local inference service (if not running) and downloads/loads the `phi-4-mini` model diff --git a/samples/js/copilot-sdk-foundry-local/src/app.ts b/samples/js/copilot-sdk-foundry-local/src/app.ts index e75935a3..0e3bd2f3 100644 --- a/samples/js/copilot-sdk-foundry-local/src/app.ts +++ b/samples/js/copilot-sdk-foundry-local/src/app.ts @@ -8,10 +8,14 @@ import * as os from "os"; const alias = "phi-4-mini"; +// Timeout for each model turn (ms). Override with FOUNDRY_TIMEOUT_MS env var. +// Local models on CPU can be slow — increase this on less powerful hardware. +const TIMEOUT_MS = Number(process.env.FOUNDRY_TIMEOUT_MS) || 120_000; + async function sendMessage( session: Awaited>, prompt: string, - timeoutMs = 120_000, + timeoutMs = TIMEOUT_MS, ) { return new Promise((resolve) => { let settled = false; diff --git a/samples/js/copilot-sdk-foundry-local/src/tool-calling.ts b/samples/js/copilot-sdk-foundry-local/src/tool-calling.ts index 5947a4bf..d1261f50 100644 --- a/samples/js/copilot-sdk-foundry-local/src/tool-calling.ts +++ b/samples/js/copilot-sdk-foundry-local/src/tool-calling.ts @@ -19,6 +19,10 @@ import * as os from "os"; const alias = "phi-4-mini"; +// Timeout for each model turn (ms). Override with FOUNDRY_TIMEOUT_MS env var. +// Local models on CPU can be slow — increase this on less powerful hardware. +const TIMEOUT_MS = Number(process.env.FOUNDRY_TIMEOUT_MS) || 120_000; + // --------------------------------------------------------------------------- // Helper: send a message and wait for the assistant's full reply. // Foundry Local streaming sometimes omits finish_reason, which causes a @@ -28,7 +32,7 @@ const alias = "phi-4-mini"; async function sendMessage( session: Awaited>, prompt: string, - timeoutMs = 120_000, + timeoutMs = TIMEOUT_MS, ) { return new Promise((resolve) => { let settled = false; From e1acb3563d25b8eb5c28cf4cf944ae76602df332 Mon Sep 17 00:00:00 2001 From: Govind Kamtamneni Date: Fri, 6 Feb 2026 12:05:08 -0800 Subject: [PATCH 8/8] Fix SDK reference link to point to Foundry Local SDK docs --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 69ed1392..ca8a9e8d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,7 +4,7 @@ The Foundry Local documentation is provided on [Microsoft Learn](https://learn.m ## API Reference -- [Foundry Local C# SDK API Reference](./cs-api/Microsoft.AI.Foundry.Local.md) +- [Foundry Local SDK Reference](https://learn.microsoft.com/en-us/azure/ai-foundry/foundry-local/reference/reference-sdk?view=foundry-classic&tabs=windows&pivots=programming-language-javascript) ## Integration Guides