@namzu/sdk 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +14 -9
  3. package/dist/constants/provider/index.d.ts +0 -3
  4. package/dist/constants/provider/index.d.ts.map +1 -1
  5. package/dist/constants/provider/index.js +0 -18
  6. package/dist/constants/provider/index.js.map +1 -1
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/provider/__tests__/registry.test.d.ts +11 -0
  12. package/dist/provider/__tests__/registry.test.d.ts.map +1 -0
  13. package/dist/provider/__tests__/registry.test.js +118 -0
  14. package/dist/provider/__tests__/registry.test.js.map +1 -0
  15. package/dist/provider/index.d.ts +3 -3
  16. package/dist/provider/index.d.ts.map +1 -1
  17. package/dist/provider/index.js +3 -3
  18. package/dist/provider/index.js.map +1 -1
  19. package/dist/provider/mock-register.d.ts +12 -0
  20. package/dist/provider/mock-register.d.ts.map +1 -0
  21. package/dist/provider/mock-register.js +24 -0
  22. package/dist/provider/mock-register.js.map +1 -0
  23. package/dist/provider/mock.d.ts +26 -0
  24. package/dist/provider/mock.d.ts.map +1 -0
  25. package/dist/provider/{factory.js → mock.js} +3 -45
  26. package/dist/provider/mock.js.map +1 -0
  27. package/dist/provider/registry.d.ts +47 -0
  28. package/dist/provider/registry.d.ts.map +1 -0
  29. package/dist/provider/registry.js +89 -0
  30. package/dist/provider/registry.js.map +1 -0
  31. package/dist/rag/rag-tool.d.ts +1 -1
  32. package/dist/types/provider/config.d.ts +29 -21
  33. package/dist/types/provider/config.d.ts.map +1 -1
  34. package/dist/types/provider/index.d.ts +1 -1
  35. package/dist/types/provider/index.d.ts.map +1 -1
  36. package/package.json +4 -2
  37. package/src/constants/provider/index.ts +0 -22
  38. package/src/index.ts +5 -4
  39. package/src/provider/__tests__/registry.test.ts +155 -0
  40. package/src/provider/index.ts +3 -3
  41. package/src/provider/mock-register.ts +27 -0
  42. package/src/provider/{factory.ts → mock.ts} +2 -57
  43. package/src/provider/registry.ts +118 -0
  44. package/src/types/provider/config.ts +31 -29
  45. package/src/types/provider/index.ts +3 -4
  46. package/dist/provider/bedrock/client.d.ts +0 -14
  47. package/dist/provider/bedrock/client.d.ts.map +0 -1
  48. package/dist/provider/bedrock/client.js +0 -460
  49. package/dist/provider/bedrock/client.js.map +0 -1
  50. package/dist/provider/factory.d.ts +0 -39
  51. package/dist/provider/factory.d.ts.map +0 -1
  52. package/dist/provider/factory.js.map +0 -1
  53. package/dist/provider/openrouter/client.d.ts +0 -17
  54. package/dist/provider/openrouter/client.d.ts.map +0 -1
  55. package/dist/provider/openrouter/client.js +0 -305
  56. package/dist/provider/openrouter/client.js.map +0 -1
  57. package/src/provider/bedrock/client.ts +0 -548
  58. package/src/provider/openrouter/client.ts +0 -390
@@ -0,0 +1,89 @@
1
+ export class UnknownProviderError extends Error {
2
+ providerType;
3
+ constructor(providerType) {
4
+ super(`Unsupported provider type: ${providerType}`);
5
+ this.name = 'UnknownProviderError';
6
+ this.providerType = providerType;
7
+ }
8
+ }
9
+ export class DuplicateProviderError extends Error {
10
+ providerType;
11
+ constructor(providerType) {
12
+ super(`Provider type "${providerType}" is already registered. Pass { replace: true } to override an existing registration.`);
13
+ this.name = 'DuplicateProviderError';
14
+ this.providerType = providerType;
15
+ }
16
+ }
17
+ // Module-private state. Only the exported functions below can read/mutate.
18
+ const providers = new Map();
19
+ const capabilities = new Map();
20
+ /**
21
+ * Central registry for LLM providers.
22
+ *
23
+ * Provider packages (@namzu/bedrock, @namzu/openai, etc.) export a
24
+ * `register<Vendor>()` function that calls `ProviderRegistry.register()`
25
+ * with a vendor-specific type string, provider class, and capabilities.
26
+ *
27
+ * The core sdk pre-registers `MockLLMProvider` under type `'mock'`.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * import { ProviderRegistry } from '@namzu/sdk'
32
+ * import { registerBedrock } from '@namzu/bedrock'
33
+ *
34
+ * registerBedrock()
35
+ *
36
+ * const { provider, capabilities } = ProviderRegistry.create({
37
+ * type: 'bedrock',
38
+ * region: 'us-east-1',
39
+ * })
40
+ * ```
41
+ */
42
+ export class ProviderRegistry {
43
+ static register(type, ctor, caps, options) {
44
+ if (providers.has(type) && !options?.replace) {
45
+ throw new DuplicateProviderError(type);
46
+ }
47
+ providers.set(type, ctor);
48
+ capabilities.set(type, caps);
49
+ }
50
+ static create(config) {
51
+ const provider = ProviderRegistry.createProvider(config);
52
+ const caps = ProviderRegistry.getCapabilities(config.type);
53
+ return { provider, capabilities: caps };
54
+ }
55
+ static createProvider(config) {
56
+ const Ctor = providers.get(config.type);
57
+ if (!Ctor) {
58
+ throw new UnknownProviderError(config.type);
59
+ }
60
+ return new Ctor(config);
61
+ }
62
+ static getCapabilities(type) {
63
+ const caps = capabilities.get(type);
64
+ if (!caps) {
65
+ throw new UnknownProviderError(type);
66
+ }
67
+ return caps;
68
+ }
69
+ static isSupported(type) {
70
+ return providers.has(type);
71
+ }
72
+ static unregister(type) {
73
+ capabilities.delete(type);
74
+ return providers.delete(type);
75
+ }
76
+ static listTypes() {
77
+ return Array.from(providers.keys());
78
+ }
79
+ }
80
+ /**
81
+ * @internal — not exported from the package barrel. Do not use in production code.
82
+ * Available to in-tree tests via relative import (`./provider/registry.js`).
83
+ * External consumers cannot reach this because `@namzu/sdk` only exports `.`.
84
+ */
85
+ export function __resetProviderRegistryInternal() {
86
+ providers.clear();
87
+ capabilities.clear();
88
+ }
89
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/provider/registry.ts"],"names":[],"mappings":"AAWA,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IACrC,YAAY,CAAQ;IAE7B,YAAY,YAAoB;QAC/B,KAAK,CAAC,8BAA8B,YAAY,EAAE,CAAC,CAAA;QACnD,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAA;QAClC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAA;IACjC,CAAC;CACD;AAED,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IACvC,YAAY,CAAQ;IAE7B,YAAY,YAAoB;QAC/B,KAAK,CACJ,kBAAkB,YAAY,uFAAuF,CACrH,CAAA;QACD,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAA;QACpC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAA;IACjC,CAAC;CACD;AAED,2EAA2E;AAC3E,MAAM,SAAS,GAAG,IAAI,GAAG,EAA2C,CAAA;AACpE,MAAM,YAAY,GAAG,IAAI,GAAG,EAAgC,CAAA;AAE5D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,gBAAgB;IAC5B,MAAM,CAAC,QAAQ,CACd,IAAO,EACP,IAAuD,EACvD,IAA0B,EAC1B,OAAyB;QAEzB,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;YAC9C,MAAM,IAAI,sBAAsB,CAAC,IAAI,CAAC,CAAA;QACvC,CAAC;QACD,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,IAAuC,CAAC,CAAA;QAC5D,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IAC7B,CAAC;IAED,MAAM,CAAC,MAAM,CAAC,MAA6B;QAC1C,MAAM,QAAQ,GAAG,gBAAgB,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;QACxD,MAAM,IAAI,GAAG,gBAAgB,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAC1D,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,CAAA;IACxC,CAAC;IAED,MAAM,CAAC,cAAc,CAAC,MAA6B;QAClD,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACvC,IAAI,CAAC,IAAI,EAAE,CAAC;YACX,MAAM,IAAI,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAC5C,CAAC;QACD,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,CAAA;IACxB,CAAC;IAED,MAAM,CAAC,eAAe,CAAC,IAAY;QAClC,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACnC,IAAI,CAAC,IAAI,EAAE,CAAC;YACX,MAAM,IAAI,oBAAoB,CAAC,IAAI,CAAC,CAAA;QACrC,CAAC;QACD,OAAO,IAAI,CAAA;IACZ,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,IAAY;QAC9B,OAAO,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC3B,CAAC;IAED,MAAM,CAAC,UAAU,CAAC,IAAkB;QACnC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACzB,OAAO,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC9B,CAAC;IAED,MAAM,CAAC,SAAS;QACf,OAAO,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAmB,CAAA;IACtD,CAAC;CACD;AAED;;;;GAIG;AACH,MAAM,UAAU,+BAA+B;IAC9C,SAAS,CAAC,KAAK,EAAE,CAAA;IACjB,YAAY,CAAC,KAAK,EAAE,CAAA;AACrB,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import type { RAGToolConfig } from '../types/rag/index.js';
2
2
  export declare function createRAGTool(config: RAGToolConfig): import("../index.js").ToolDefinition<{
3
3
  query: string;
4
- top_k?: number | undefined;
5
4
  knowledge_base_id?: string | undefined;
5
+ top_k?: number | undefined;
6
6
  }>;
7
7
  //# sourceMappingURL=rag-tool.d.ts.map
@@ -1,32 +1,35 @@
1
1
  import type { LLMProvider } from './interface.js';
2
- export type ProviderType = 'openrouter' | 'bedrock' | 'mock';
3
- export interface OpenRouterConfig {
4
- apiKey: string;
5
- baseUrl?: string;
6
- siteUrl?: string;
7
- siteName?: string;
8
- timeout?: number;
9
- }
10
- export interface OpenRouterProviderConfig extends OpenRouterConfig {
11
- type: 'openrouter';
12
- }
13
- export interface BedrockConfig {
14
- region?: string;
15
- accessKeyId?: string;
16
- secretAccessKey?: string;
17
- sessionToken?: string;
18
- timeout?: number;
19
- }
20
- export interface BedrockProviderConfig extends BedrockConfig {
21
- type: 'bedrock';
2
+ /**
3
+ * Registry of provider config shapes keyed by provider type string.
4
+ *
5
+ * Third-party provider packages extend this interface via TypeScript module
6
+ * augmentation. Each key in the registry becomes a valid `ProviderType`, and
7
+ * consumers of `ProviderRegistry.create({ type: 'X', ... })` get discriminated
8
+ * union narrowing to the correct config shape.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * // @namzu/bedrock package
13
+ * declare module '@namzu/sdk' {
14
+ * interface ProviderConfigRegistry {
15
+ * bedrock: BedrockProviderConfig
16
+ * }
17
+ * }
18
+ * ```
19
+ */
20
+ export interface ProviderConfigRegistry {
21
+ mock: MockProviderConfig;
22
22
  }
23
+ export type ProviderType = keyof ProviderConfigRegistry & string;
24
+ export type ProviderFactoryConfig = {
25
+ [K in ProviderType]: ProviderConfigRegistry[K];
26
+ }[ProviderType];
23
27
  export interface MockProviderConfig {
24
28
  type: 'mock';
25
29
  model?: string;
26
30
  responseText?: string;
27
31
  responseDelayMs?: number;
28
32
  }
29
- export type ProviderFactoryConfig = OpenRouterProviderConfig | BedrockProviderConfig | MockProviderConfig;
30
33
  export interface ProviderCapabilities {
31
34
  supportsTools: boolean;
32
35
  supportsStreaming: boolean;
@@ -37,4 +40,9 @@ export interface ProviderFactoryResult {
37
40
  provider: LLMProvider;
38
41
  capabilities: ProviderCapabilities;
39
42
  }
43
+ export interface RegisterOptions {
44
+ /** When true, replace an existing registration. Default false → throw on duplicate. */
45
+ replace?: boolean;
46
+ }
47
+ export type LLMProviderConstructor<C = unknown> = new (config: C) => LLMProvider;
40
48
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/types/provider/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,SAAS,GAAG,MAAM,CAAA;AAE5D,MAAM,WAAW,gBAAgB;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IACjE,IAAI,EAAE,YAAY,CAAA;CAClB;AAED,MAAM,WAAW,aAAa;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,YAAY,CAAC,EAAE,MAAM,CAAA;IAErB,OAAO,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,qBAAsB,SAAQ,aAAa;IAC3D,IAAI,EAAE,SAAS,CAAA;CACf;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,MAAM,qBAAqB,GAC9B,wBAAwB,GACxB,qBAAqB,GACrB,kBAAkB,CAAA;AAErB,MAAM,WAAW,oBAAoB;IACpC,aAAa,EAAE,OAAO,CAAA;IACtB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,uBAAuB,EAAE,OAAO,CAAA;IAChC,eAAe,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,WAAW,CAAA;IACrB,YAAY,EAAE,oBAAoB,CAAA;CAClC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/types/provider/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,kBAAkB,CAAA;CACxB;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,sBAAsB,GAAG,MAAM,CAAA;AAEhE,MAAM,MAAM,qBAAqB,GAAG;KAClC,CAAC,IAAI,YAAY,GAAG,sBAAsB,CAAC,CAAC,CAAC;CAC9C,CAAC,YAAY,CAAC,CAAA;AAEf,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,oBAAoB;IACpC,aAAa,EAAE,OAAO,CAAA;IACtB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,uBAAuB,EAAE,OAAO,CAAA;IAChC,eAAe,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,WAAW,CAAA;IACrB,YAAY,EAAE,oBAAoB,CAAA;CAClC;AAED,MAAM,WAAW,eAAe;IAC/B,uFAAuF;IACvF,OAAO,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,MAAM,sBAAsB,CAAC,CAAC,GAAG,OAAO,IAAI,KAAK,MAAM,EAAE,CAAC,KAAK,WAAW,CAAA"}
@@ -2,5 +2,5 @@ export type { ToolChoice, ResponseFormat, CacheControl, ChatCompletionParams, Ch
2
2
  export type { StreamChunk } from './stream.js';
3
3
  export type { ModelInfo } from './model.js';
4
4
  export type { LLMProvider } from './interface.js';
5
- export type { ProviderType, OpenRouterConfig, OpenRouterProviderConfig, BedrockConfig, BedrockProviderConfig, MockProviderConfig, ProviderFactoryConfig, ProviderCapabilities, ProviderFactoryResult, } from './config.js';
5
+ export type { ProviderConfigRegistry, ProviderType, MockProviderConfig, ProviderFactoryConfig, ProviderCapabilities, ProviderFactoryResult, RegisterOptions, LLMProviderConstructor, } from './config.js';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/provider/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,UAAU,EACV,cAAc,EACd,YAAY,EACZ,oBAAoB,EACpB,sBAAsB,GACtB,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjD,YAAY,EACX,YAAY,EACZ,gBAAgB,EAChB,wBAAwB,EACxB,aAAa,EACb,qBAAqB,EACrB,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,GACrB,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/provider/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,UAAU,EACV,cAAc,EACd,YAAY,EACZ,oBAAoB,EACpB,sBAAsB,GACtB,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjD,YAAY,EACX,sBAAsB,EACtB,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,sBAAsB,GACtB,MAAM,aAAa,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@namzu/sdk",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Open-source AI agent SDK with a built-in runtime. Nothing between you and your agents.",
5
5
  "license": "FSL-1.1-MIT",
6
6
  "type": "module",
@@ -27,12 +27,14 @@
27
27
  "README.md",
28
28
  "CHANGELOG.md"
29
29
  ],
30
+ "sideEffects": [
31
+ "./dist/provider/mock-register.js"
32
+ ],
30
33
  "publishConfig": {
31
34
  "access": "public",
32
35
  "provenance": true
33
36
  },
34
37
  "dependencies": {
35
- "@aws-sdk/client-bedrock-runtime": "^3.700.0",
36
38
  "@opentelemetry/api": "^1.9.0",
37
39
  "@opentelemetry/exporter-metrics-otlp-http": "^0.57.0",
38
40
  "@opentelemetry/exporter-trace-otlp-http": "^0.57.0",
@@ -1,23 +1 @@
1
- import type { ProviderCapabilities, ProviderType } from '../../types/provider/index.js'
2
-
3
1
  export const FALLBACK_MOCK_MODEL = 'mock-model'
4
-
5
- export const PROVIDER_CAPABILITIES: Record<ProviderType, ProviderCapabilities> = {
6
- openrouter: {
7
- supportsTools: true,
8
- supportsStreaming: true,
9
- supportsFunctionCalling: true,
10
- },
11
- bedrock: {
12
- supportsTools: true,
13
- supportsStreaming: true,
14
- supportsFunctionCalling: true,
15
- },
16
- mock: {
17
- supportsTools: false,
18
- supportsStreaming: true,
19
- supportsFunctionCalling: false,
20
- },
21
- }
22
-
23
- export const OPENROUTER_BASE_URL = process.env.OPENROUTER_BASE_URL ?? 'https://openrouter.ai/api/v1'
package/src/index.ts CHANGED
@@ -180,11 +180,12 @@ export { buildMemoryTools } from './tools/memory/index.js'
180
180
  export { LocalTaskGateway } from './gateway/local.js'
181
181
 
182
182
  export {
183
- OpenRouterProvider,
184
- BedrockProvider,
185
- ProviderFactory,
186
- MockLLMProvider,
183
+ ProviderRegistry,
187
184
  UnknownProviderError,
185
+ DuplicateProviderError,
186
+ MockLLMProvider,
187
+ registerMock,
188
+ MOCK_CAPABILITIES,
188
189
  } from './provider/index.js'
189
190
 
190
191
  export {
@@ -0,0 +1,155 @@
1
+ import { beforeEach, describe, expect, it } from 'vitest'
2
+ import type { LLMProvider, ProviderCapabilities } from '../../types/provider/index.js'
3
+ import { registerMock } from '../mock-register.js'
4
+ import { MockLLMProvider } from '../mock.js'
5
+ import {
6
+ DuplicateProviderError,
7
+ ProviderRegistry,
8
+ UnknownProviderError,
9
+ __resetProviderRegistryInternal,
10
+ } from '../registry.js'
11
+
12
+ // Simulate a downstream provider package's type registration via module augmentation.
13
+ // This is the pattern @namzu/bedrock, @namzu/openai, etc. use in their own code.
14
+ interface TestProviderConfig {
15
+ type: 'test'
16
+ value?: string
17
+ }
18
+
19
+ declare module '../../types/provider/config.js' {
20
+ interface ProviderConfigRegistry {
21
+ test: TestProviderConfig
22
+ }
23
+ }
24
+
25
+ class TestProvider implements LLMProvider {
26
+ readonly id = 'test'
27
+ readonly name = 'Test'
28
+ constructor(public readonly config: TestProviderConfig) {}
29
+ async chat() {
30
+ return {
31
+ id: 'x',
32
+ model: 'm',
33
+ message: { role: 'assistant' as const, content: 'ok' },
34
+ finishReason: 'stop' as const,
35
+ usage: {
36
+ promptTokens: 0,
37
+ completionTokens: 0,
38
+ totalTokens: 0,
39
+ cachedTokens: 0,
40
+ cacheWriteTokens: 0,
41
+ },
42
+ }
43
+ }
44
+ async *chatStream() {
45
+ yield { id: 'x', delta: { content: 'ok' } }
46
+ }
47
+ }
48
+
49
+ const TEST_CAPS: ProviderCapabilities = {
50
+ supportsTools: false,
51
+ supportsStreaming: true,
52
+ supportsFunctionCalling: false,
53
+ }
54
+
55
+ describe('ProviderRegistry', () => {
56
+ beforeEach(() => {
57
+ __resetProviderRegistryInternal()
58
+ registerMock()
59
+ })
60
+
61
+ describe('register', () => {
62
+ it('registers a provider and stores capabilities', () => {
63
+ ProviderRegistry.register('test', TestProvider, {
64
+ supportsTools: true,
65
+ supportsStreaming: false,
66
+ supportsFunctionCalling: true,
67
+ })
68
+
69
+ expect(ProviderRegistry.isSupported('test')).toBe(true)
70
+ expect(ProviderRegistry.getCapabilities('test').supportsTools).toBe(true)
71
+ })
72
+
73
+ it('throws DuplicateProviderError on re-register without replace', () => {
74
+ ProviderRegistry.register('test', TestProvider, TEST_CAPS)
75
+ expect(() => ProviderRegistry.register('test', TestProvider, TEST_CAPS)).toThrowError(
76
+ DuplicateProviderError,
77
+ )
78
+ })
79
+
80
+ it('allows replacement when { replace: true }', () => {
81
+ ProviderRegistry.register('test', TestProvider, TEST_CAPS)
82
+ const newCaps: ProviderCapabilities = {
83
+ supportsTools: true,
84
+ supportsStreaming: true,
85
+ supportsFunctionCalling: true,
86
+ }
87
+ expect(() =>
88
+ ProviderRegistry.register('test', TestProvider, newCaps, { replace: true }),
89
+ ).not.toThrow()
90
+ expect(ProviderRegistry.getCapabilities('test').supportsTools).toBe(true)
91
+ })
92
+ })
93
+
94
+ describe('create', () => {
95
+ it('instantiates the registered provider with typed config', () => {
96
+ const { provider, capabilities } = ProviderRegistry.create({
97
+ type: 'mock',
98
+ model: 'test-model',
99
+ })
100
+ expect(provider).toBeInstanceOf(MockLLMProvider)
101
+ expect(capabilities.supportsStreaming).toBe(true)
102
+ })
103
+
104
+ it('throws UnknownProviderError for unregistered type', () => {
105
+ expect(() =>
106
+ ProviderRegistry.create({ type: 'nonexistent' } as unknown as { type: 'mock' }),
107
+ ).toThrowError(UnknownProviderError)
108
+ })
109
+ })
110
+
111
+ describe('isSupported', () => {
112
+ it('returns true for registered types', () => {
113
+ expect(ProviderRegistry.isSupported('mock')).toBe(true)
114
+ })
115
+
116
+ it('returns false for unregistered types', () => {
117
+ expect(ProviderRegistry.isSupported('nope')).toBe(false)
118
+ })
119
+ })
120
+
121
+ describe('unregister', () => {
122
+ it('removes provider and capabilities', () => {
123
+ ProviderRegistry.register('test', TestProvider, TEST_CAPS)
124
+ expect(ProviderRegistry.unregister('test')).toBe(true)
125
+ expect(ProviderRegistry.isSupported('test')).toBe(false)
126
+ })
127
+
128
+ it('returns false when type is not registered', () => {
129
+ expect(ProviderRegistry.unregister('test')).toBe(false)
130
+ })
131
+ })
132
+
133
+ describe('listTypes', () => {
134
+ it('returns all registered types', () => {
135
+ ProviderRegistry.register('test', TestProvider, TEST_CAPS)
136
+ const types = ProviderRegistry.listTypes()
137
+ expect(types).toContain('mock')
138
+ expect(types).toContain('test')
139
+ })
140
+ })
141
+
142
+ describe('error types', () => {
143
+ it('UnknownProviderError preserves type', () => {
144
+ const err = new UnknownProviderError('foo')
145
+ expect(err.providerType).toBe('foo')
146
+ expect(err.name).toBe('UnknownProviderError')
147
+ })
148
+
149
+ it('DuplicateProviderError preserves type', () => {
150
+ const err = new DuplicateProviderError('bar')
151
+ expect(err.providerType).toBe('bar')
152
+ expect(err.name).toBe('DuplicateProviderError')
153
+ })
154
+ })
155
+ })
@@ -1,3 +1,3 @@
1
- export { OpenRouterProvider } from './openrouter/client.js'
2
- export { BedrockProvider } from './bedrock/client.js'
3
- export { ProviderFactory, MockLLMProvider, UnknownProviderError } from './factory.js'
1
+ export { ProviderRegistry, UnknownProviderError, DuplicateProviderError } from './registry.js'
2
+ export { MockLLMProvider } from './mock.js'
3
+ export { registerMock, MOCK_CAPABILITIES } from './mock-register.js'
@@ -0,0 +1,27 @@
1
+ import type { ProviderCapabilities } from '../types/provider/index.js'
2
+ import { MockLLMProvider } from './mock.js'
3
+ import { ProviderRegistry } from './registry.js'
4
+
5
+ export const MOCK_CAPABILITIES: ProviderCapabilities = {
6
+ supportsTools: false,
7
+ supportsStreaming: true,
8
+ supportsFunctionCalling: false,
9
+ }
10
+
11
+ /**
12
+ * Register the built-in MockLLMProvider under type `'mock'`.
13
+ *
14
+ * This is invoked automatically on `@namzu/sdk` import (side-effect whitelisted
15
+ * via `package.json#sideEffects`). Users never need to call this explicitly.
16
+ *
17
+ * Exposed for tests that reset the registry via `ProviderRegistry._reset()`.
18
+ */
19
+ export function registerMock(): void {
20
+ if (!ProviderRegistry.isSupported('mock')) {
21
+ ProviderRegistry.register('mock', MockLLMProvider, MOCK_CAPABILITIES)
22
+ }
23
+ }
24
+
25
+ // Auto-register on module load. This module is listed in sdk's
26
+ // `package.json#sideEffects` so bundlers preserve this line under tree-shaking.
27
+ registerMock()
@@ -1,20 +1,14 @@
1
- import { FALLBACK_MOCK_MODEL, PROVIDER_CAPABILITIES } from '../constants/provider/index.js'
1
+ import { FALLBACK_MOCK_MODEL } from '../constants/provider/index.js'
2
2
  import type { TokenUsage } from '../types/common/index.js'
3
3
  import type {
4
4
  ChatCompletionParams,
5
5
  ChatCompletionResponse,
6
6
  LLMProvider,
7
7
  MockProviderConfig,
8
- ProviderCapabilities,
9
- ProviderFactoryConfig,
10
- ProviderFactoryResult,
11
- ProviderType,
12
8
  StreamChunk,
13
9
  } from '../types/provider/index.js'
14
- import { BedrockProvider } from './bedrock/client.js'
15
- import { OpenRouterProvider } from './openrouter/client.js'
16
10
 
17
- class MockLLMProvider implements LLMProvider {
11
+ export class MockLLMProvider implements LLMProvider {
18
12
  readonly id = 'mock'
19
13
  readonly name = 'Mock LLM Provider'
20
14
 
@@ -103,52 +97,3 @@ class MockLLMProvider implements LLMProvider {
103
97
  return true
104
98
  }
105
99
  }
106
-
107
- export class UnknownProviderError extends Error {
108
- readonly providerType: string
109
-
110
- constructor(providerType: string) {
111
- super(`Unsupported provider type: ${providerType}`)
112
- this.name = 'UnknownProviderError'
113
- this.providerType = providerType
114
- }
115
- }
116
-
117
- export class ProviderFactory {
118
- static create(config: ProviderFactoryConfig): ProviderFactoryResult {
119
- const provider = ProviderFactory.createProvider(config)
120
- const capabilities = ProviderFactory.getCapabilities(config.type)
121
- return { provider, capabilities }
122
- }
123
-
124
- static createProvider(config: ProviderFactoryConfig): LLMProvider {
125
- if (config.type === 'openrouter') {
126
- const { type, ...openrouterConfig } = config
127
- return new OpenRouterProvider(openrouterConfig)
128
- }
129
- if (config.type === 'bedrock') {
130
- const { type, ...bedrockConfig } = config
131
- return new BedrockProvider(bedrockConfig)
132
- }
133
- if (config.type === 'mock') {
134
- return new MockLLMProvider(config)
135
- }
136
-
137
- throw new UnknownProviderError((config as { type: string }).type)
138
- }
139
-
140
- static getCapabilities(type: ProviderType): ProviderCapabilities {
141
- const capabilities = PROVIDER_CAPABILITIES[type]
142
- if (!capabilities) {
143
- throw new UnknownProviderError(type)
144
- }
145
-
146
- return capabilities
147
- }
148
-
149
- static isSupported(type: string): type is ProviderType {
150
- return type === 'openrouter' || type === 'bedrock' || type === 'mock'
151
- }
152
- }
153
-
154
- export { OpenRouterProvider, BedrockProvider, MockLLMProvider }
@@ -0,0 +1,118 @@
1
+ import type {
2
+ LLMProvider,
3
+ LLMProviderConstructor,
4
+ ProviderCapabilities,
5
+ ProviderConfigRegistry,
6
+ ProviderFactoryConfig,
7
+ ProviderFactoryResult,
8
+ ProviderType,
9
+ RegisterOptions,
10
+ } from '../types/provider/index.js'
11
+
12
+ export class UnknownProviderError extends Error {
13
+ readonly providerType: string
14
+
15
+ constructor(providerType: string) {
16
+ super(`Unsupported provider type: ${providerType}`)
17
+ this.name = 'UnknownProviderError'
18
+ this.providerType = providerType
19
+ }
20
+ }
21
+
22
+ export class DuplicateProviderError extends Error {
23
+ readonly providerType: string
24
+
25
+ constructor(providerType: string) {
26
+ super(
27
+ `Provider type "${providerType}" is already registered. Pass { replace: true } to override an existing registration.`,
28
+ )
29
+ this.name = 'DuplicateProviderError'
30
+ this.providerType = providerType
31
+ }
32
+ }
33
+
34
+ // Module-private state. Only the exported functions below can read/mutate.
35
+ const providers = new Map<string, LLMProviderConstructor<unknown>>()
36
+ const capabilities = new Map<string, ProviderCapabilities>()
37
+
38
+ /**
39
+ * Central registry for LLM providers.
40
+ *
41
+ * Provider packages (@namzu/bedrock, @namzu/openai, etc.) export a
42
+ * `register<Vendor>()` function that calls `ProviderRegistry.register()`
43
+ * with a vendor-specific type string, provider class, and capabilities.
44
+ *
45
+ * The core sdk pre-registers `MockLLMProvider` under type `'mock'`.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * import { ProviderRegistry } from '@namzu/sdk'
50
+ * import { registerBedrock } from '@namzu/bedrock'
51
+ *
52
+ * registerBedrock()
53
+ *
54
+ * const { provider, capabilities } = ProviderRegistry.create({
55
+ * type: 'bedrock',
56
+ * region: 'us-east-1',
57
+ * })
58
+ * ```
59
+ */
60
+ export class ProviderRegistry {
61
+ static register<K extends ProviderType>(
62
+ type: K,
63
+ ctor: LLMProviderConstructor<ProviderConfigRegistry[K]>,
64
+ caps: ProviderCapabilities,
65
+ options?: RegisterOptions,
66
+ ): void {
67
+ if (providers.has(type) && !options?.replace) {
68
+ throw new DuplicateProviderError(type)
69
+ }
70
+ providers.set(type, ctor as LLMProviderConstructor<unknown>)
71
+ capabilities.set(type, caps)
72
+ }
73
+
74
+ static create(config: ProviderFactoryConfig): ProviderFactoryResult {
75
+ const provider = ProviderRegistry.createProvider(config)
76
+ const caps = ProviderRegistry.getCapabilities(config.type)
77
+ return { provider, capabilities: caps }
78
+ }
79
+
80
+ static createProvider(config: ProviderFactoryConfig): LLMProvider {
81
+ const Ctor = providers.get(config.type)
82
+ if (!Ctor) {
83
+ throw new UnknownProviderError(config.type)
84
+ }
85
+ return new Ctor(config)
86
+ }
87
+
88
+ static getCapabilities(type: string): ProviderCapabilities {
89
+ const caps = capabilities.get(type)
90
+ if (!caps) {
91
+ throw new UnknownProviderError(type)
92
+ }
93
+ return caps
94
+ }
95
+
96
+ static isSupported(type: string): type is ProviderType {
97
+ return providers.has(type)
98
+ }
99
+
100
+ static unregister(type: ProviderType): boolean {
101
+ capabilities.delete(type)
102
+ return providers.delete(type)
103
+ }
104
+
105
+ static listTypes(): ProviderType[] {
106
+ return Array.from(providers.keys()) as ProviderType[]
107
+ }
108
+ }
109
+
110
+ /**
111
+ * @internal — not exported from the package barrel. Do not use in production code.
112
+ * Available to in-tree tests via relative import (`./provider/registry.js`).
113
+ * External consumers cannot reach this because `@namzu/sdk` only exports `.`.
114
+ */
115
+ export function __resetProviderRegistryInternal(): void {
116
+ providers.clear()
117
+ capabilities.clear()
118
+ }