{"id":50708039,"url":"https://github.com/posit-dev/ai-provider-bridge","last_synced_at":"2026-06-09T13:01:58.860Z","repository":{"id":360521254,"uuid":"1245826250","full_name":"posit-dev/ai-provider-bridge","owner":"posit-dev","description":"Platform-neutral provider infrastructure for LLM access","archived":false,"fork":false,"pushed_at":"2026-06-05T16:17:11.000Z","size":322,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-05T18:33:04.696Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/posit-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-21T15:39:54.000Z","updated_at":"2026-06-05T16:17:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/posit-dev/ai-provider-bridge","commit_stats":null,"previous_names":["posit-dev/ai-provider-bridge"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/posit-dev/ai-provider-bridge","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/posit-dev%2Fai-provider-bridge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/posit-dev%2Fai-provider-bridge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/posit-dev%2Fai-provider-bridge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/posit-dev%2Fai-provider-bridge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/posit-dev","download_url":"https://codeload.github.com/posit-dev/ai-provider-bridge/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/posit-dev%2Fai-provider-bridge/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34107866,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-09T13:01:55.016Z","updated_at":"2026-06-09T13:01:58.853Z","avatar_url":"https://github.com/posit-dev.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ai-provider-bridge\n\nPlatform-neutral provider infrastructure for AI model access: registry, model clients, credential abstractions, and provider registration functions, with no dependency on VS Code or Node platform services.\n\n## Package Boundaries\n\nThese rules keep the dependency graph clean:\n\n- **Root entrypoint must not import `vscode`** -- only the `/positron` entrypoint may.\n- **Must not import from consumer packages** -- host applications depend on this package, not the reverse.\n\n## Entrypoints\n\n| Entrypoint                              | What it provides                                                                                                                                     | Heavy deps?           |\n| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |\n| `ai-provider-bridge`                    | `ProviderRegistry`, interfaces (`ModelClient`, `CredentialProvider`, `StepLogger`), `createCachedModelFetcher`, provider map, `LocalProviderManager` | No                    |\n| `ai-provider-bridge/providers`          | `register*Provider()` functions, client classes, helpers                                                                                             | Yes (AI SDK packages) |\n| `ai-provider-bridge/providers-external` | Minimal provider set (external/OSS builds -- Posit AI only)                                                                                          | Minimal               |\n| `ai-provider-bridge/positron`           | `PositronCredentialProvider`, `VscodeLmClient`, `listVscodeLmModels()`, `fromAiMessages2()`, LM helpers                                              | Yes (`vscode`)        |\n\n## Supported Providers\n\n| Provider          | Client             | Model Discovery |\n| ----------------- | ------------------ | --------------- |\n| Anthropic         | AnthropicClient    | Cached via API  |\n| OpenAI            | OpenAIClient       | Cached via API  |\n| AWS Bedrock       | BedrockClient      | Cached via API  |\n| Google Gemini     | GeminiClient       | Cached via API  |\n| Google Vertex AI  | GoogleVertexClient | Cached via API  |\n| GitHub Copilot    | CopilotSdkClient   | Via Copilot SDK |\n| DeepSeek          | DeepSeekClient     | Cached via API  |\n| OpenRouter        | OpenRouterClient   | Cached via API  |\n| Ollama (local)    | OllamaClient       | Cached via API  |\n| LM Studio (local) | LMStudioClient     | Cached via API  |\n| Snowflake Cortex  | SnowflakeClient    | Static          |\n| Posit AI          | PositAiClient      | Cached via API  |\n| OpenAI-Compatible | OpenAIClient       | User-configured |\n| Foundry           | OpenAIClient       | User-configured |\n\n## API Reference\n\n### ProviderRegistry\n\nPlugin system for registering model fetchers and client factories per provider. Created once at startup and passed to `NodeModelService` (or used directly).\n\n```ts\nimport { ProviderRegistry } from \"ai-provider-bridge\";\n\nconst registry = new ProviderRegistry(logger);\n```\n\n#### Methods\n\n| Method                  | Signature                                                                                   | Description                                                           |\n| ----------------------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| `registerModelFetcher`  | `(providerId: string, fetcher: ModelFetcher) =\u003e void`                                       | Register a function that returns available models for a provider      |\n| `registerClientFactory` | `(providerId: string, factory: ClientFactory) =\u003e void`                                      | Register a function that creates a `ModelClient` for a provider       |\n| `getModelsForProvider`  | `(providerId: string, credentials: ProviderCredentials, metadata?) =\u003e Promise\u003cModelInfo[]\u003e` | Fetch models (returns `[]` if provider not registered or fetch fails) |\n| `getClientForProvider`  | `(providerId: string, credentials: ProviderCredentials) =\u003e ModelClient \\| null`             | Create a client (returns `null` if provider not registered)           |\n| `clearAllModelCaches`   | `() =\u003e void`                                                                                | Clear all provider-level model caches (call on credential change)     |\n| `clearModelCache`       | `(providerId: string) =\u003e void`                                                              | Clear a single provider's model cache                                 |\n\n#### Types\n\n```ts\n// Function that fetches available models for a provider\ntype ModelFetcher = (\n  credentials: ProviderCredentials,\n  metadata?: Record\u003cstring, unknown\u003e,\n) =\u003e Promise\u003cModelInfo[]\u003e;\n\n// Function that creates an API client for a provider\ntype ClientFactory = (credentials: ProviderCredentials) =\u003e ModelClient;\n```\n\n### ModelClient\n\nInterface that all LLM clients implement. Returns an async iterable of `LMStreamPart` (AI SDK `TextStreamPart`).\n\n```ts\ninterface ModelClient {\n  chat(params: {\n    model: string;\n    messages: ModelMessage[];\n    systemPrompt?: string;\n    maxOutputTokens?: number;\n    tools?: Record\u003cstring, AiToolWithJsonSchema\u003e;\n    cancellationToken: CancellationToken;\n    thinkingEffort?: string;\n    contextLength?: number;\n    webSearchEnabled?: boolean;\n\n    // Posit Assistant-specific parameters\n    metadata?: { sessionId?: string; conversationId?: string };\n    stepLoggers?: StepLogger[];\n  }): Promise\u003cAsyncIterable\u003cLMStreamPart\u003e\u003e;\n}\n```\n\n### CredentialProvider\n\nPlatform-agnostic credential access. The `/positron` entrypoint provides a VS Code implementation; other platforms use `NodeModelService`'s credential resolution directly.\n\n```ts\ninterface CredentialProvider {\n  getCredentials(providerId: ProviderId): Promise\u003cProviderCredentials | null\u003e;\n  onDidChangeCredentials(callback: (providerIds: ProviderId[]) =\u003e void): Disposable;\n}\n```\n\n### StepLogger\n\nInterface for logging LLM API call steps (request/response data, token usage). Multiple loggers can be used simultaneously (e.g., JSON files + CSV).\n\n```ts\ninterface StepLogger {\n  logStep(data: StepLogData): Promise\u003cvoid\u003e;\n  reportCreditsDepleted?(): void; // optional: 402 from gateway\n  reportAgreementRequired?(): void; // optional: 403 from gateway\n}\n\ninterface StepLogData {\n  callId: string;\n  stepIndex: number;\n  provider: string;\n  model: string;\n  request: unknown;\n  response: unknown;\n  usage: LanguageModelUsage;\n  providerMetadata?: Record\u003cstring, unknown\u003e;\n  headers: Record\u003cstring, string\u003e;\n}\n```\n\n### createCachedModelFetcher\n\nFactory for building model fetchers with TTL caching and a three-level fallback strategy (fresh fetch \u003e stale cache \u003e static fallback models).\n\n```ts\nimport { createCachedModelFetcher } from \"ai-provider-bridge\";\n\nconst fetcher = createCachedModelFetcher\u003cApiKeyCredentials\u003e({\n  providerId: \"my-provider\",\n  apiUrl: \"https://api.example.com/v1/models\",   // or resolveUrl for dynamic endpoints\n  hasCredentials: (creds) =\u003e Boolean(creds.apiKey),\n  createHeaders: (creds) =\u003e ({ Authorization: `Bearer ${creds.apiKey}` }),\n  parseResponse: (data) =\u003e /* transform API response to ModelInfo[] */,\n  fallbackModels: [{ id: \"default-model\", name: \"Default\", ... }],\n  ttl: 60 * 60 * 1000,  // default: 60 minutes\n  logger,\n});\n\n// Register it\nregistry.registerModelFetcher(\"my-provider\", fetcher);\n\n// Invalidate cache when credentials change\nfetcher.clearCache?.();\n```\n\n### LocalProviderManager\n\nManages endpoint configuration for local LLM providers (Ollama, LM Studio). Uses dependency injection for all I/O -- no platform-specific or `vscode` dependencies. Endpoints are stored in `~/.positai/settings.json` under `providers.{providerId}.endpoint` and cached in-memory for synchronous reads.\n\n```ts\nimport { LocalProviderManager, LOCAL_PROVIDER_IDS, isLocalProviderId } from \"ai-provider-bridge\";\n\n// LOCAL_PROVIDER_IDS: readonly [\"ollama\", \"lmstudio\"]\n\nconst manager = new LocalProviderManager({\n  readSettings: async () =\u003e {\n    /* read and parse settings.json, return object or undefined */\n  },\n  mutateSettings: async (mutator) =\u003e {\n    /* read-mutate-write settings.json atomically */\n  },\n  watchSettings: (onChange) =\u003e {\n    /* watch settings file, return { dispose() } */\n  },\n  isEnabled: () =\u003e true,\n  watchEnabled: (onChange) =\u003e ({ dispose: () =\u003e {} }),\n  logger: { warn: console.warn, info: console.info },\n});\n\nawait manager.initialize(); // read disk + start watchers\n\nmanager.getEndpoint(\"ollama\"); // synchronous, from cache\nawait manager.setEndpoint(\"ollama\", \"http://localhost:11434\");\nawait manager.clearEndpoint(\"ollama\");\n\nmanager.onDidChange((providerIds) =\u003e {\n  // React to endpoint or feature-gate changes\n});\n\nmanager.dispose(); // stop watchers\n```\n\n### Provider Map\n\nStatic mapping of provider IDs to Positron auth provider configuration. Exported from the root entrypoint (no `vscode` dependency).\n\n```ts\nimport { PROVIDER_MAP, MAPPED_PROVIDER_IDS } from \"ai-provider-bridge\";\n\n// MAPPED_PROVIDER_IDS: readonly ProviderId[]\n// e.g. [\"anthropic\", \"positai\", \"openai\", \"gemini\", \"openai-compatible\",\n//        \"bedrock\", \"ms-foundry\", \"snowflake-cortex\"]\n\n// PROVIDER_MAP: Partial\u003cRecord\u003cProviderId, AuthProviderMapping\u003e\u003e\n// Each entry has: { authProviderId, scopes, credentialType }\n```\n\n### Credential Types\n\n```ts\ntype ProviderCredentials =\n  | ApiKeyCredentials // { type: \"apikey\"; apiKey: string; baseUrl?: string }\n  | OAuthCredentials // { type: \"oauth\"; accessToken: string; baseUrl?: string }\n  | AwsCredentials // { type: \"aws-credentials\"; region: string; accessKeyId: string; ... }\n  | GoogleCloudCredentials // { type: \"google-cloud\"; projectId?: string; location?: string }\n  | LocalCredentials; // { type: \"local\"; endpoint: string }\n```\n\n## Caching\n\nThe package provides built-in caching at multiple levels:\n\n### Model Listing Cache (Automatic)\n\n`createCachedModelFetcher` is used internally by most `register*Provider` functions. It provides:\n\n- **60-minute TTL** by default (configurable)\n- **Three-level fallback**: fresh API fetch \u003e stale cache \u003e static fallback models\n- **Cache invalidation** via `registry.clearModelCache(providerId)` or `registry.clearAllModelCaches()`\n\n```ts\n// Models are cached automatically -- repeated calls use the cache\nconst models = await registry.getModelsForProvider(\"anthropic\", credentials);\n\n// Force cache invalidation (e.g., when credentials change)\nregistry.clearModelCache(\"anthropic\");\n\n// Or clear all provider caches at once\nregistry.clearAllModelCaches();\n```\n\n### Registry Instance (Consumer Responsibility)\n\nThe `ProviderRegistry` should be created once at application startup and reused for the lifetime of the process. Provider registrations are stored in in-memory maps -- creating multiple registries wastes memory and loses cache state.\n\n```ts\n// app startup -- create once\nconst registry = new ProviderRegistry(logger);\nregisterAnthropicProvider(registry, logger);\nregisterOpenAIProvider(registry, logger);\nregisterBedrockProvider(registry, logger);\n\n// Export for use across the application\nexport { registry };\n```\n\n### Client Instances\n\n`getClientForProvider()` creates a new `ModelClient` on each call. For API-key providers (Anthropic, OpenAI, etc.), this is lightweight and stateless -- no caching needed. For expensive providers (e.g., Copilot, which spawns a subprocess), the provider's factory function handles internal caching with auth-state transition detection.\n\n## Dependencies\n\nThis package uses types from the [Vercel AI SDK](https://sdk.vercel.ai/) (`ai` package) in its public API:\n\n- **`ModelMessage`** (from `ai`) -- the message type for `ModelClient.chat()` input. Consumers must import this from `ai` directly.\n- **`LMStreamPart`** (from `ai-provider-bridge`) -- the stream output type, an alias for `ai.TextStreamPart\u003cRecord\u003cstring, ai.Tool\u003e\u003e`. Consumers import this from ai-provider-bridge.\n\nThe `ai` package is a regular dependency (bundled), so consumers don't need to install it separately unless they reference `ai` types directly in their own code.\n\n### Consumer `package.json`\n\nA package that uses ai-provider-bridge to send LLM requests needs:\n\n```jsonc\n{\n  \"dependencies\": {\n    \"ai-provider-bridge\": \"*\",\n    \"ai\": \"^6.0.68\", // Only if you reference ModelMessage or other ai types directly\n  },\n}\n```\n\nIf you import `ai-provider-bridge/providers` to register specific providers, the corresponding AI SDK provider packages must also be installed. These are listed as optional peer dependencies:\n\n```jsonc\n{\n  \"dependencies\": {\n    \"ai-provider-bridge\": \"*\",\n    // Only include the provider SDK(s) you use:\n    \"@ai-sdk/anthropic\": \"^3.0.70\", // for registerAnthropicProvider\n    \"@ai-sdk/openai\": \"^3.0.25\", // for registerOpenAIProvider\n    \"@ai-sdk/google\": \"^3.0.20\", // for registerGeminiProvider\n    // etc.\n  },\n}\n```\n\n### Peer Dependency Matrix\n\n| Provider             | Required peer deps                                                                   |\n| -------------------- | ------------------------------------------------------------------------------------ |\n| Anthropic            | `@ai-sdk/anthropic`                                                                  |\n| OpenAI               | `@ai-sdk/openai`                                                                     |\n| Bedrock              | `@ai-sdk/amazon-bedrock`, `@aws-sdk/client-bedrock`, `@aws-sdk/credential-providers` |\n| Gemini               | `@ai-sdk/google`                                                                     |\n| Google Vertex        | `@ai-sdk/google-vertex`, `google-auth-library`                                       |\n| Copilot              | `@github/copilot-sdk`                                                                |\n| DeepSeek             | `@ai-sdk/deepseek`                                                                   |\n| OpenRouter           | `@openrouter/ai-sdk-provider`                                                        |\n| Ollama               | `ai-sdk-ollama`                                                                      |\n| OpenAI-Compatible    | `@ai-sdk/openai-compatible`                                                          |\n| Positron entry point | `vscode`                                                                             |\n\n## Usage Examples\n\n### Sending a request and streaming the response\n\n```ts\nimport type { ModelMessage } from \"ai\";\nimport { ProviderRegistry } from \"ai-provider-bridge\";\nimport type { LMStreamPart } from \"ai-provider-bridge\";\nimport { registerAnthropicProvider } from \"ai-provider-bridge/providers\";\n\n// 1. Set up registry and register a provider\nconst registry = new ProviderRegistry(logger);\nregisterAnthropicProvider(registry, logger);\n\nconst credentials = { type: \"apikey\" as const, apiKey: \"sk-...\" };\n\n// 2. Create a client\nconst client = registry.getClientForProvider(\"anthropic\", credentials);\n\n// 3. Build messages using the AI SDK's ModelMessage type\nconst messages: ModelMessage[] = [\n  { role: \"user\", content: [{ type: \"text\", text: \"What is 2 + 2?\" }] },\n];\n\n// 4. Stream the response\nconst stream = await client!.chat({\n  model: \"claude-sonnet-4-5-20250929\",\n  messages,\n  cancellationToken: { onCancellationRequested: () =\u003e ({ dispose() {} }) },\n});\n\nfor await (const part of stream) {\n  switch (part.type) {\n    case \"text-delta\":\n      process.stdout.write(part.textDelta);\n      break;\n    case \"reasoning\":\n      // Extended thinking content (when thinkingEffort is set)\n      break;\n    case \"tool-call\":\n      console.log(`Tool call: ${part.toolName}(${JSON.stringify(part.args)})`);\n      break;\n    case \"finish\":\n      console.log(`\\nDone. Usage: ${JSON.stringify(part.usage)}`);\n      break;\n  }\n}\n```\n\n### Multi-turn conversation\n\n```ts\nimport type { ModelMessage } from \"ai\";\nimport { ProviderRegistry } from \"ai-provider-bridge\";\nimport { registerAnthropicProvider } from \"ai-provider-bridge/providers\";\n\nconst registry = new ProviderRegistry(logger);\nregisterAnthropicProvider(registry, logger);\n\nconst credentials = { type: \"apikey\" as const, apiKey: \"sk-...\" };\nconst client = registry.getClientForProvider(\"anthropic\", credentials)!;\nconst cancellationToken = { onCancellationRequested: () =\u003e ({ dispose() {} }) };\n\n// Build up a conversation history\nconst messages: ModelMessage[] = [\n  { role: \"user\", content: [{ type: \"text\", text: \"Remember the number 42.\" }] },\n];\n\n// First turn\nlet response = \"\";\nfor await (const part of await client.chat({\n  model: \"claude-sonnet-4-5-20250929\",\n  messages,\n  cancellationToken,\n})) {\n  if (part.type === \"text-delta\") response += part.textDelta;\n}\n\n// Append assistant response and next user message\nmessages.push(\n  { role: \"assistant\", content: [{ type: \"text\", text: response }] },\n  { role: \"user\", content: [{ type: \"text\", text: \"What number did I ask you to remember?\" }] },\n);\n\n// Second turn\nfor await (const part of await client.chat({\n  model: \"claude-sonnet-4-5-20250929\",\n  messages,\n  cancellationToken,\n})) {\n  if (part.type === \"text-delta\") process.stdout.write(part.textDelta);\n}\n```\n\n### Fetching available models\n\n```ts\nimport { ProviderRegistry } from \"ai-provider-bridge\";\nimport { registerAnthropicProvider } from \"ai-provider-bridge/providers\";\n\nconst registry = new ProviderRegistry(logger);\nregisterAnthropicProvider(registry, logger);\n\nconst models = await registry.getModelsForProvider(\"anthropic\", {\n  type: \"apikey\",\n  apiKey: \"sk-...\",\n});\n\nfor (const model of models) {\n  console.log(`${model.id} -- ${model.name}`);\n}\n```\n\n### Node platforms (Standalone, RStudio, TUI, Desktop)\n\nCreate a `ProviderRegistry`, register providers, and pass it to your platform's model service:\n\n```ts\nimport { ProviderRegistry } from \"ai-provider-bridge\";\nimport { registerAnthropicProvider, registerOpenAIProvider } from \"ai-provider-bridge/providers\";\n\n// 1. Create registry\nconst registry = new ProviderRegistry(logger);\n\n// 2. Register providers\nregisterAnthropicProvider(registry, logger);\nregisterOpenAIProvider(registry, logger);\n\n// 3. Pass to your platform's model service layer\nconst modelService = createModelService({\n  defaultModel: \"claude-sonnet-4-5-20250929\",\n  pluginRegistry: registry,\n  logger,\n});\n```\n\n### Positron extension (VS Code auth bridge)\n\n```ts\nimport { ProviderRegistry, MAPPED_PROVIDER_IDS } from \"ai-provider-bridge\";\nimport { registerAnthropicProvider } from \"ai-provider-bridge/providers\";\nimport { PositronCredentialProvider } from \"ai-provider-bridge/positron\";\n\nconst registry = new ProviderRegistry(logger);\nregisterAnthropicProvider(registry, logger);\n\nconst credentialProvider = new PositronCredentialProvider();\n\n// Fetch models for each mapped provider that has credentials\nfor (const providerId of MAPPED_PROVIDER_IDS) {\n  const credentials = await credentialProvider.getCredentials(providerId);\n  if (credentials) {\n    const models = await registry.getModelsForProvider(providerId, credentials);\n    // ...\n  }\n}\n\n// React to credential changes\ncredentialProvider.onDidChangeCredentials((providerIds) =\u003e {\n  for (const id of providerIds) {\n    registry.clearModelCache(id);\n  }\n});\n```\n\n### VS Code Language Model client (vscode.lm)\n\nThe `/positron` entrypoint also provides `VscodeLmClient` -- a `ModelClient` implementation that wraps VS Code's Language Model API. This lets any VS Code extension send requests to `vscode.lm` models (e.g., Copilot) through the standard `ModelClient.chat()` interface.\n\n```ts\nimport type { ModelMessage } from \"ai\";\nimport { VscodeLmClient, listVscodeLmModels } from \"ai-provider-bridge/positron\";\n\n// 1. List available vscode.lm models (optionally filter by provider)\nconst models = await listVscodeLmModels({ providerIds: [\"copilot\"] });\n\n// 2. Select a model from VS Code\nconst vscodeLmModels = await vscode.lm.selectChatModels({ id: models[0].id });\n\n// 3. Create a client wrapping that model\nconst client = new VscodeLmClient(vscodeLmModels[0], logger);\n\n// 4. Use the standard ModelClient interface\nconst messages: ModelMessage[] = [{ role: \"user\", content: [{ type: \"text\", text: \"Hello\" }] }];\nconst stream = await client.chat({\n  model: models[0].id,\n  messages,\n  cancellationToken: { onCancellationRequested: () =\u003e ({ dispose() {} }) },\n});\n\nfor await (const part of stream) {\n  if (part.type === \"text-delta\") process.stdout.write(part.textDelta);\n}\n```\n\n`listVscodeLmModels()` enriches models with capability information (token limits, tool/image support, thinking effort levels) using the same provider-specific helpers as the direct API path.\n\nThe `/positron` entrypoint also exports message conversion utilities:\n\n- **`fromAiMessages2()`** -- converts AI SDK `ModelMessage[]` to `LanguageModelChatMessage2[]`\n- **`hasAnthropicCacheControl()` / `setAnthropicCacheControl()`** -- cache marker helpers\n- **LM part helpers** -- `isCacheBreakpointPart()`, `cacheBreakpointPart()`, type guards\n\n## Internal / External Build Variants\n\nExternal builds alias provider files to their `-external` variants via the consuming monorepo's build configuration:\n\n- `providers.ts` -\u003e `providers-external.ts` -- only Posit AI provider (keeps non-positai provider code and SDK dependencies out of the bundle)\n- `types.ts` -\u003e `types-external.ts` -- only positai provider ID and notification actions\n- `local-providers.ts` -\u003e `local-providers-external.ts` -- empty `LOCAL_PROVIDER_IDS` and no-op `LocalProviderManager` (excludes Ollama/LM Studio)\n\n## Adding a New Provider\n\n1. Create a client class in `src/model-clients/`\n2. Create a provider module in `src/providers/` with a `register*Provider()` function\n3. Export both from `src/providers.ts`\n4. If it needs Positron auth mapping, add to `PROVIDER_MAP` in `src/provider-map.ts`\n5. Register it in your host application's provider setup\n\n## Development\n\n```bash\nnpm install\nnpm run build          # Bundled build (esbuild + declaration emit)\nnpm run build:unbundled # tsc only (for debugging)\nnpm run check-types    # Type check without emit\nnpm run test           # Run tests (vitest)\nnpm run test:watch     # Watch mode\nnpm run clean          # Remove dist/ and build artifacts\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fposit-dev%2Fai-provider-bridge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fposit-dev%2Fai-provider-bridge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fposit-dev%2Fai-provider-bridge/lists"}