gavio 0.1.0

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 (196) hide show
  1. package/README.md +95 -0
  2. package/dist/cjs/context.js +47 -0
  3. package/dist/cjs/errors.js +57 -0
  4. package/dist/cjs/gateway.js +127 -0
  5. package/dist/cjs/ids.js +60 -0
  6. package/dist/cjs/index.js +49 -0
  7. package/dist/cjs/interceptors/audit/index.js +12 -0
  8. package/dist/cjs/interceptors/audit/interceptor.js +77 -0
  9. package/dist/cjs/interceptors/audit/record.js +107 -0
  10. package/dist/cjs/interceptors/audit/sink.js +3 -0
  11. package/dist/cjs/interceptors/audit/sinks/index.js +5 -0
  12. package/dist/cjs/interceptors/audit/sinks/stdout.js +33 -0
  13. package/dist/cjs/interceptors/base.js +7 -0
  14. package/dist/cjs/interceptors/cache/backend.js +9 -0
  15. package/dist/cjs/interceptors/cache/backends/index.js +5 -0
  16. package/dist/cjs/interceptors/cache/backends/memory.js +53 -0
  17. package/dist/cjs/interceptors/cache/index.js +9 -0
  18. package/dist/cjs/interceptors/chain.js +57 -0
  19. package/dist/cjs/interceptors/index.js +18 -0
  20. package/dist/cjs/interceptors/pii/context.js +25 -0
  21. package/dist/cjs/interceptors/pii/guard.js +161 -0
  22. package/dist/cjs/interceptors/pii/index.js +28 -0
  23. package/dist/cjs/interceptors/pii/match.js +21 -0
  24. package/dist/cjs/interceptors/pii/scanner.js +31 -0
  25. package/dist/cjs/interceptors/pii/scanners/bsn.js +41 -0
  26. package/dist/cjs/interceptors/pii/scanners/credit-card.js +51 -0
  27. package/dist/cjs/interceptors/pii/scanners/email.js +26 -0
  28. package/dist/cjs/interceptors/pii/scanners/iban.js +58 -0
  29. package/dist/cjs/interceptors/pii/scanners/index.js +45 -0
  30. package/dist/cjs/interceptors/pii/scanners/ip-address.js +36 -0
  31. package/dist/cjs/interceptors/pii/scanners/phone.js +37 -0
  32. package/dist/cjs/interceptors/pii/scanners/secret.js +46 -0
  33. package/dist/cjs/interceptors/pii/scanners/ssn.js +28 -0
  34. package/dist/cjs/interceptors/reliability/fallback.js +53 -0
  35. package/dist/cjs/interceptors/reliability/index.js +11 -0
  36. package/dist/cjs/interceptors/reliability/retry.js +69 -0
  37. package/dist/cjs/interceptors/reliability/timeout.js +41 -0
  38. package/dist/cjs/package.json +3 -0
  39. package/dist/cjs/pricing.js +70 -0
  40. package/dist/cjs/providers/anthropic.js +80 -0
  41. package/dist/cjs/providers/base.js +30 -0
  42. package/dist/cjs/providers/http.js +42 -0
  43. package/dist/cjs/providers/index.js +34 -0
  44. package/dist/cjs/providers/mock.js +54 -0
  45. package/dist/cjs/providers/openai.js +63 -0
  46. package/dist/cjs/request.js +60 -0
  47. package/dist/cjs/response.js +55 -0
  48. package/dist/cjs/testing/harness.js +70 -0
  49. package/dist/cjs/testing/index.js +8 -0
  50. package/dist/cjs/types.js +61 -0
  51. package/dist/esm/context.d.ts +33 -0
  52. package/dist/esm/context.js +43 -0
  53. package/dist/esm/errors.d.ts +36 -0
  54. package/dist/esm/errors.js +44 -0
  55. package/dist/esm/gateway.d.ts +54 -0
  56. package/dist/esm/gateway.js +123 -0
  57. package/dist/esm/ids.d.ts +11 -0
  58. package/dist/esm/ids.js +56 -0
  59. package/dist/esm/index.d.ts +25 -0
  60. package/dist/esm/index.js +20 -0
  61. package/dist/esm/interceptors/audit/index.d.ts +7 -0
  62. package/dist/esm/interceptors/audit/index.js +3 -0
  63. package/dist/esm/interceptors/audit/interceptor.d.ts +11 -0
  64. package/dist/esm/interceptors/audit/interceptor.js +72 -0
  65. package/dist/esm/interceptors/audit/record.d.ts +66 -0
  66. package/dist/esm/interceptors/audit/record.js +103 -0
  67. package/dist/esm/interceptors/audit/sink.d.ts +8 -0
  68. package/dist/esm/interceptors/audit/sink.js +2 -0
  69. package/dist/esm/interceptors/audit/sinks/index.d.ts +2 -0
  70. package/dist/esm/interceptors/audit/sinks/index.js +1 -0
  71. package/dist/esm/interceptors/audit/sinks/stdout.d.ts +8 -0
  72. package/dist/esm/interceptors/audit/sinks/stdout.js +30 -0
  73. package/dist/esm/interceptors/base.d.ts +37 -0
  74. package/dist/esm/interceptors/base.js +4 -0
  75. package/dist/esm/interceptors/cache/backend.d.ts +14 -0
  76. package/dist/esm/interceptors/cache/backend.js +8 -0
  77. package/dist/esm/interceptors/cache/backends/index.d.ts +2 -0
  78. package/dist/esm/interceptors/cache/backends/index.js +1 -0
  79. package/dist/esm/interceptors/cache/backends/memory.d.ts +7 -0
  80. package/dist/esm/interceptors/cache/backends/memory.js +50 -0
  81. package/dist/esm/interceptors/cache/index.d.ts +7 -0
  82. package/dist/esm/interceptors/cache/index.js +5 -0
  83. package/dist/esm/interceptors/chain.d.ts +17 -0
  84. package/dist/esm/interceptors/chain.js +53 -0
  85. package/dist/esm/interceptors/index.d.ts +8 -0
  86. package/dist/esm/interceptors/index.js +7 -0
  87. package/dist/esm/interceptors/pii/context.d.ts +15 -0
  88. package/dist/esm/interceptors/pii/context.js +21 -0
  89. package/dist/esm/interceptors/pii/guard.d.ts +30 -0
  90. package/dist/esm/interceptors/pii/guard.js +157 -0
  91. package/dist/esm/interceptors/pii/index.d.ts +10 -0
  92. package/dist/esm/interceptors/pii/index.js +7 -0
  93. package/dist/esm/interceptors/pii/match.d.ts +26 -0
  94. package/dist/esm/interceptors/pii/match.js +17 -0
  95. package/dist/esm/interceptors/pii/scanner.d.ts +32 -0
  96. package/dist/esm/interceptors/pii/scanner.js +26 -0
  97. package/dist/esm/interceptors/pii/scanners/bsn.d.ts +5 -0
  98. package/dist/esm/interceptors/pii/scanners/bsn.js +37 -0
  99. package/dist/esm/interceptors/pii/scanners/credit-card.d.ts +4 -0
  100. package/dist/esm/interceptors/pii/scanners/credit-card.js +47 -0
  101. package/dist/esm/interceptors/pii/scanners/email.d.ts +3 -0
  102. package/dist/esm/interceptors/pii/scanners/email.js +23 -0
  103. package/dist/esm/interceptors/pii/scanners/iban.d.ts +5 -0
  104. package/dist/esm/interceptors/pii/scanners/iban.js +54 -0
  105. package/dist/esm/interceptors/pii/scanners/index.d.ts +13 -0
  106. package/dist/esm/interceptors/pii/scanners/index.js +30 -0
  107. package/dist/esm/interceptors/pii/scanners/ip-address.d.ts +3 -0
  108. package/dist/esm/interceptors/pii/scanners/ip-address.js +33 -0
  109. package/dist/esm/interceptors/pii/scanners/phone.d.ts +6 -0
  110. package/dist/esm/interceptors/pii/scanners/phone.js +34 -0
  111. package/dist/esm/interceptors/pii/scanners/secret.d.ts +9 -0
  112. package/dist/esm/interceptors/pii/scanners/secret.js +43 -0
  113. package/dist/esm/interceptors/pii/scanners/ssn.d.ts +3 -0
  114. package/dist/esm/interceptors/pii/scanners/ssn.js +25 -0
  115. package/dist/esm/interceptors/reliability/fallback.d.ts +9 -0
  116. package/dist/esm/interceptors/reliability/fallback.js +50 -0
  117. package/dist/esm/interceptors/reliability/index.d.ts +7 -0
  118. package/dist/esm/interceptors/reliability/index.js +4 -0
  119. package/dist/esm/interceptors/reliability/retry.d.ts +13 -0
  120. package/dist/esm/interceptors/reliability/retry.js +66 -0
  121. package/dist/esm/interceptors/reliability/timeout.d.ts +9 -0
  122. package/dist/esm/interceptors/reliability/timeout.js +37 -0
  123. package/dist/esm/package.json +3 -0
  124. package/dist/esm/pricing.d.ts +19 -0
  125. package/dist/esm/pricing.js +65 -0
  126. package/dist/esm/providers/anthropic.d.ts +30 -0
  127. package/dist/esm/providers/anthropic.js +77 -0
  128. package/dist/esm/providers/base.d.ts +23 -0
  129. package/dist/esm/providers/base.js +28 -0
  130. package/dist/esm/providers/http.d.ts +8 -0
  131. package/dist/esm/providers/http.js +39 -0
  132. package/dist/esm/providers/index.d.ts +15 -0
  133. package/dist/esm/providers/index.js +25 -0
  134. package/dist/esm/providers/mock.d.ts +31 -0
  135. package/dist/esm/providers/mock.js +51 -0
  136. package/dist/esm/providers/openai.d.ts +26 -0
  137. package/dist/esm/providers/openai.js +60 -0
  138. package/dist/esm/request.d.ts +36 -0
  139. package/dist/esm/request.js +56 -0
  140. package/dist/esm/response.d.ts +38 -0
  141. package/dist/esm/response.js +51 -0
  142. package/dist/esm/testing/harness.d.ts +37 -0
  143. package/dist/esm/testing/harness.js +66 -0
  144. package/dist/esm/testing/index.d.ts +5 -0
  145. package/dist/esm/testing/index.js +3 -0
  146. package/dist/esm/types.d.ts +58 -0
  147. package/dist/esm/types.js +56 -0
  148. package/package.json +115 -0
  149. package/src/context.ts +57 -0
  150. package/src/errors.ts +47 -0
  151. package/src/gateway.ts +174 -0
  152. package/src/ids.ts +69 -0
  153. package/src/index.ts +52 -0
  154. package/src/interceptors/audit/index.ts +7 -0
  155. package/src/interceptors/audit/interceptor.ts +93 -0
  156. package/src/interceptors/audit/record.ts +138 -0
  157. package/src/interceptors/audit/sink.ts +10 -0
  158. package/src/interceptors/audit/sinks/index.ts +2 -0
  159. package/src/interceptors/audit/sinks/stdout.ts +42 -0
  160. package/src/interceptors/base.ts +58 -0
  161. package/src/interceptors/cache/backend.ts +15 -0
  162. package/src/interceptors/cache/backends/index.ts +2 -0
  163. package/src/interceptors/cache/backends/memory.ts +68 -0
  164. package/src/interceptors/cache/index.ts +8 -0
  165. package/src/interceptors/chain.ts +65 -0
  166. package/src/interceptors/index.ts +9 -0
  167. package/src/interceptors/pii/context.ts +24 -0
  168. package/src/interceptors/pii/guard.ts +201 -0
  169. package/src/interceptors/pii/index.ts +21 -0
  170. package/src/interceptors/pii/match.ts +43 -0
  171. package/src/interceptors/pii/scanner.ts +54 -0
  172. package/src/interceptors/pii/scanners/bsn.ts +44 -0
  173. package/src/interceptors/pii/scanners/credit-card.ts +52 -0
  174. package/src/interceptors/pii/scanners/email.ts +31 -0
  175. package/src/interceptors/pii/scanners/iban.ts +60 -0
  176. package/src/interceptors/pii/scanners/index.ts +35 -0
  177. package/src/interceptors/pii/scanners/ip-address.ts +41 -0
  178. package/src/interceptors/pii/scanners/phone.ts +46 -0
  179. package/src/interceptors/pii/scanners/secret.ts +51 -0
  180. package/src/interceptors/pii/scanners/ssn.ts +33 -0
  181. package/src/interceptors/reliability/fallback.ts +66 -0
  182. package/src/interceptors/reliability/index.ts +8 -0
  183. package/src/interceptors/reliability/retry.ts +97 -0
  184. package/src/interceptors/reliability/timeout.ts +53 -0
  185. package/src/pricing.ts +72 -0
  186. package/src/providers/anthropic.ts +113 -0
  187. package/src/providers/base.ts +52 -0
  188. package/src/providers/http.ts +50 -0
  189. package/src/providers/index.ts +39 -0
  190. package/src/providers/mock.ts +73 -0
  191. package/src/providers/openai.ts +94 -0
  192. package/src/request.ts +76 -0
  193. package/src/response.ts +73 -0
  194. package/src/testing/harness.ts +98 -0
  195. package/src/testing/index.ts +6 -0
  196. package/src/types.ts +83 -0
@@ -0,0 +1,77 @@
1
+ /** anthropicAdapter — Messages API (Claude Sonnet, Haiku, Opus). */
2
+ import { ConfigurationError } from '../errors.js';
3
+ import { TokenUsage } from '../types.js';
4
+ import { BaseProviderAdapter } from './base.js';
5
+ import { postJson } from './http.js';
6
+ const DEFAULT_BASE_URL = 'https://api.anthropic.com/v1';
7
+ const API_VERSION = '2023-06-01';
8
+ /**
9
+ * Talks to the Anthropic Messages endpoint. Anthropic splits the system prompt
10
+ * from the message list, so any `role === "system"` messages are extracted into
11
+ * the `system` field.
12
+ */
13
+ class AnthropicAdapter extends BaseProviderAdapter {
14
+ apiKey;
15
+ baseUrl;
16
+ timeoutSeconds;
17
+ constructor(options = {}) {
18
+ super(options.pricing);
19
+ this.apiKey = options.apiKey ?? process.env['ANTHROPIC_API_KEY'];
20
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, '');
21
+ this.timeoutSeconds = (options.timeoutMs ?? 30_000) / 1000;
22
+ }
23
+ get providerName() {
24
+ return 'anthropic';
25
+ }
26
+ headers() {
27
+ if (!this.apiKey) {
28
+ throw new ConfigurationError('ANTHROPIC_API_KEY not set (pass apiKey or set the env var)');
29
+ }
30
+ return {
31
+ 'x-api-key': this.apiKey,
32
+ 'anthropic-version': API_VERSION,
33
+ };
34
+ }
35
+ static splitSystem(messages) {
36
+ const systemParts = messages
37
+ .filter((m) => m.role === 'system')
38
+ .map((m) => m.content);
39
+ const chat = messages.filter((m) => m.role !== 'system');
40
+ const system = systemParts.length > 0 ? systemParts.join('\n') : null;
41
+ return [system, chat];
42
+ }
43
+ async complete(request) {
44
+ const started = performance.now();
45
+ const [system, chat] = AnthropicAdapter.splitSystem(request.messages);
46
+ const payload = {
47
+ model: request.model,
48
+ messages: chat,
49
+ max_tokens: request.maxTokens,
50
+ temperature: request.temperature,
51
+ };
52
+ if (system)
53
+ payload['system'] = system;
54
+ const data = await postJson(`${this.baseUrl}/messages`, payload, this.headers(), this.timeoutSeconds);
55
+ const blocks = data['content'] ?? [];
56
+ const content = blocks
57
+ .filter((b) => b['type'] === 'text')
58
+ .map((b) => b['text'] ?? '')
59
+ .join('');
60
+ const usageData = data['usage'] ?? {};
61
+ const usage = new TokenUsage(usageData['input_tokens'] ?? 0, usageData['output_tokens'] ?? 0);
62
+ return this.buildResponse(request, content, usage, data['model'] ?? request.model, started);
63
+ }
64
+ async healthCheck() {
65
+ try {
66
+ this.headers();
67
+ return true;
68
+ }
69
+ catch {
70
+ return false;
71
+ }
72
+ }
73
+ }
74
+ /** Factory: build an Anthropic provider adapter. */
75
+ export function anthropicAdapter(options = {}) {
76
+ return new AnthropicAdapter(options);
77
+ }
@@ -0,0 +1,23 @@
1
+ /** ProviderAdapter interface and shared response-building helpers. */
2
+ import { PricingProvider } from '../pricing.js';
3
+ import { GavioRequest } from '../request.js';
4
+ import { GavioResponse } from '../response.js';
5
+ import { TokenUsage } from '../types.js';
6
+ /** Adapter to one LLM provider. */
7
+ export interface ProviderAdapter {
8
+ readonly providerName: string;
9
+ complete(request: GavioRequest): Promise<GavioResponse>;
10
+ stream?(request: GavioRequest): AsyncIterable<string>;
11
+ healthCheck(): Promise<boolean>;
12
+ readonly reportedModelVersion?: string | null;
13
+ }
14
+ /** Base class with a shared pricing provider and response builder. */
15
+ export declare abstract class BaseProviderAdapter implements ProviderAdapter {
16
+ protected readonly pricing: PricingProvider;
17
+ constructor(pricing?: PricingProvider);
18
+ abstract get providerName(): string;
19
+ abstract complete(request: GavioRequest): Promise<GavioResponse>;
20
+ abstract healthCheck(): Promise<boolean>;
21
+ get reportedModelVersion(): string | null;
22
+ protected buildResponse(request: GavioRequest, content: string, usage: TokenUsage, modelVersion: string, startedAt: number): GavioResponse;
23
+ }
@@ -0,0 +1,28 @@
1
+ /** ProviderAdapter interface and shared response-building helpers. */
2
+ import { PricingProvider } from '../pricing.js';
3
+ import { GavioRequest } from '../request.js';
4
+ import { GavioResponse } from '../response.js';
5
+ import { TokenUsage } from '../types.js';
6
+ /** Base class with a shared pricing provider and response builder. */
7
+ export class BaseProviderAdapter {
8
+ pricing;
9
+ constructor(pricing) {
10
+ this.pricing = pricing ?? new PricingProvider();
11
+ }
12
+ get reportedModelVersion() {
13
+ return null;
14
+ }
15
+ buildResponse(request, content, usage, modelVersion, startedAt) {
16
+ const latencyMs = Math.floor(performance.now() - startedAt);
17
+ return new GavioResponse({
18
+ traceId: request.traceId,
19
+ content,
20
+ model: request.model,
21
+ provider: this.providerName,
22
+ modelVersion: modelVersion || request.model,
23
+ usage,
24
+ costUsd: this.pricing.estimate(request.model, usage),
25
+ latencyMs,
26
+ });
27
+ }
28
+ }
@@ -0,0 +1,8 @@
1
+ /** Tiny JSON-over-HTTP helper built on native fetch (keeps core dependency-free). */
2
+ /**
3
+ * POST `payload` as JSON and return the parsed response.
4
+ *
5
+ * Maps HTTP status families onto Gavio's transient error types so the
6
+ * retry/fallback policies can react.
7
+ */
8
+ export declare function postJson(url: string, payload: unknown, headers: Record<string, string>, timeoutSeconds?: number): Promise<Record<string, unknown>>;
@@ -0,0 +1,39 @@
1
+ /** Tiny JSON-over-HTTP helper built on native fetch (keeps core dependency-free). */
2
+ import { ProviderUnavailableError, RateLimitError, ServerError, } from '../errors.js';
3
+ /**
4
+ * POST `payload` as JSON and return the parsed response.
5
+ *
6
+ * Maps HTTP status families onto Gavio's transient error types so the
7
+ * retry/fallback policies can react.
8
+ */
9
+ export async function postJson(url, payload, headers, timeoutSeconds = 30.0) {
10
+ const controller = new AbortController();
11
+ const timer = setTimeout(() => controller.abort(), timeoutSeconds * 1000);
12
+ let resp;
13
+ try {
14
+ resp = await fetch(url, {
15
+ method: 'POST',
16
+ headers: { 'Content-Type': 'application/json', ...headers },
17
+ body: JSON.stringify(payload),
18
+ signal: controller.signal,
19
+ });
20
+ }
21
+ catch (error) {
22
+ const reason = error instanceof Error ? error.message : String(error);
23
+ throw new ProviderUnavailableError(`network error: ${reason}`);
24
+ }
25
+ finally {
26
+ clearTimeout(timer);
27
+ }
28
+ if (!resp.ok) {
29
+ const body = (await resp.text().catch(() => '')).slice(0, 200);
30
+ if (resp.status === 429) {
31
+ throw new RateLimitError(`429 from provider: ${body}`);
32
+ }
33
+ if (resp.status >= 500) {
34
+ throw new ServerError(`${resp.status} from provider: ${body}`);
35
+ }
36
+ throw new ProviderUnavailableError(`${resp.status} from provider: ${body}`);
37
+ }
38
+ return (await resp.json());
39
+ }
@@ -0,0 +1,15 @@
1
+ /** Provider adapters and the provider registry. */
2
+ import type { PricingProvider } from '../pricing.js';
3
+ import { Provider } from '../types.js';
4
+ import type { ProviderAdapter } from './base.js';
5
+ export type { ProviderAdapter } from './base.js';
6
+ export { BaseProviderAdapter } from './base.js';
7
+ export { mockProvider } from './mock.js';
8
+ export type { MockProviderOptions } from './mock.js';
9
+ export { openaiAdapter } from './openai.js';
10
+ export type { OpenAIAdapterOptions } from './openai.js';
11
+ export { anthropicAdapter } from './anthropic.js';
12
+ export type { AnthropicAdapterOptions } from './anthropic.js';
13
+ export { Provider } from '../types.js';
14
+ /** Instantiate the default adapter for a provider id. v0.1.0: OpenAI, Anthropic, Mock. */
15
+ export declare function buildAdapter(provider: Provider | string, pricing?: PricingProvider): ProviderAdapter;
@@ -0,0 +1,25 @@
1
+ /** Provider adapters and the provider registry. */
2
+ import { ConfigurationError } from '../errors.js';
3
+ import { Provider, coerceProvider } from '../types.js';
4
+ import { anthropicAdapter } from './anthropic.js';
5
+ import { mockProvider } from './mock.js';
6
+ import { openaiAdapter } from './openai.js';
7
+ export { BaseProviderAdapter } from './base.js';
8
+ export { mockProvider } from './mock.js';
9
+ export { openaiAdapter } from './openai.js';
10
+ export { anthropicAdapter } from './anthropic.js';
11
+ export { Provider } from '../types.js';
12
+ /** Instantiate the default adapter for a provider id. v0.1.0: OpenAI, Anthropic, Mock. */
13
+ export function buildAdapter(provider, pricing) {
14
+ const p = coerceProvider(provider);
15
+ switch (p) {
16
+ case Provider.OPENAI:
17
+ return openaiAdapter(pricing ? { pricing } : {});
18
+ case Provider.ANTHROPIC:
19
+ return anthropicAdapter(pricing ? { pricing } : {});
20
+ case Provider.MOCK:
21
+ return mockProvider(pricing ? { pricing } : {});
22
+ default:
23
+ throw new ConfigurationError(`Provider '${p}' is not available in v0.1.0 (available: openai, anthropic, mock)`);
24
+ }
25
+ }
@@ -0,0 +1,31 @@
1
+ /** mockProvider — deterministic, offline provider for dev mode and tests. */
2
+ import { PricingProvider } from '../pricing.js';
3
+ import type { GavioRequest } from '../request.js';
4
+ import type { GavioResponse } from '../response.js';
5
+ import { BaseProviderAdapter } from './base.js';
6
+ export interface MockProviderOptions {
7
+ response?: string | null;
8
+ modelVersion?: string;
9
+ pricing?: PricingProvider;
10
+ }
11
+ /**
12
+ * Returns a canned response without any network call.
13
+ *
14
+ * If `response` is null/undefined, it echoes the last user message so the
15
+ * pipeline (including PII restore) is observable end to end.
16
+ */
17
+ declare class MockProvider extends BaseProviderAdapter {
18
+ private readonly response;
19
+ private readonly modelVersion;
20
+ constructor(options?: MockProviderOptions);
21
+ get providerName(): string;
22
+ get reportedModelVersion(): string | null;
23
+ private contentFor;
24
+ complete(request: GavioRequest): Promise<GavioResponse>;
25
+ stream(request: GavioRequest): AsyncIterable<string>;
26
+ healthCheck(): Promise<boolean>;
27
+ }
28
+ /** Factory: build a mock provider adapter. */
29
+ export declare function mockProvider(options?: MockProviderOptions): ProviderAdapterMock;
30
+ export type ProviderAdapterMock = MockProvider;
31
+ export {};
@@ -0,0 +1,51 @@
1
+ /** mockProvider — deterministic, offline provider for dev mode and tests. */
2
+ import { PricingProvider, estimateTokens } from '../pricing.js';
3
+ import { TokenUsage } from '../types.js';
4
+ import { BaseProviderAdapter } from './base.js';
5
+ /**
6
+ * Returns a canned response without any network call.
7
+ *
8
+ * If `response` is null/undefined, it echoes the last user message so the
9
+ * pipeline (including PII restore) is observable end to end.
10
+ */
11
+ class MockProvider extends BaseProviderAdapter {
12
+ response;
13
+ modelVersion;
14
+ constructor(options = {}) {
15
+ super(options.pricing);
16
+ this.response = options.response ?? null;
17
+ this.modelVersion = options.modelVersion ?? 'mock-1';
18
+ }
19
+ get providerName() {
20
+ return 'mock';
21
+ }
22
+ get reportedModelVersion() {
23
+ return this.modelVersion;
24
+ }
25
+ contentFor(request) {
26
+ if (this.response !== null)
27
+ return this.response;
28
+ const lastUser = [...request.messages]
29
+ .reverse()
30
+ .find((m) => m.role === 'user');
31
+ return `[mock reply] ${lastUser?.content ?? ''}`;
32
+ }
33
+ async complete(request) {
34
+ const started = performance.now();
35
+ const content = this.contentFor(request);
36
+ const usage = new TokenUsage(estimateTokens(request.promptText()), estimateTokens(content));
37
+ return this.buildResponse(request, content, usage, this.modelVersion, started);
38
+ }
39
+ async *stream(request) {
40
+ for (const token of this.contentFor(request).split(' ')) {
41
+ yield token + ' ';
42
+ }
43
+ }
44
+ async healthCheck() {
45
+ return true;
46
+ }
47
+ }
48
+ /** Factory: build a mock provider adapter. */
49
+ export function mockProvider(options = {}) {
50
+ return new MockProvider(options);
51
+ }
@@ -0,0 +1,26 @@
1
+ /** openaiAdapter — Chat Completions API (GPT-4o, o1, ...). */
2
+ import type { PricingProvider } from '../pricing.js';
3
+ import type { GavioRequest } from '../request.js';
4
+ import type { GavioResponse } from '../response.js';
5
+ import { BaseProviderAdapter } from './base.js';
6
+ export interface OpenAIAdapterOptions {
7
+ apiKey?: string;
8
+ baseUrl?: string;
9
+ timeoutMs?: number;
10
+ organization?: string;
11
+ pricing?: PricingProvider;
12
+ }
13
+ declare class OpenAIAdapter extends BaseProviderAdapter {
14
+ private readonly apiKey;
15
+ private readonly baseUrl;
16
+ private readonly timeoutSeconds;
17
+ private readonly organization;
18
+ constructor(options?: OpenAIAdapterOptions);
19
+ get providerName(): string;
20
+ private headers;
21
+ complete(request: GavioRequest): Promise<GavioResponse>;
22
+ healthCheck(): Promise<boolean>;
23
+ }
24
+ /** Factory: build an OpenAI provider adapter. */
25
+ export declare function openaiAdapter(options?: OpenAIAdapterOptions): OpenAIAdapter;
26
+ export {};
@@ -0,0 +1,60 @@
1
+ /** openaiAdapter — Chat Completions API (GPT-4o, o1, ...). */
2
+ import { ConfigurationError } from '../errors.js';
3
+ import { TokenUsage } from '../types.js';
4
+ import { BaseProviderAdapter } from './base.js';
5
+ import { postJson } from './http.js';
6
+ const DEFAULT_BASE_URL = 'https://api.openai.com/v1';
7
+ class OpenAIAdapter extends BaseProviderAdapter {
8
+ apiKey;
9
+ baseUrl;
10
+ timeoutSeconds;
11
+ organization;
12
+ constructor(options = {}) {
13
+ super(options.pricing);
14
+ this.apiKey = options.apiKey ?? process.env['OPENAI_API_KEY'];
15
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, '');
16
+ this.timeoutSeconds = (options.timeoutMs ?? 30_000) / 1000;
17
+ this.organization = options.organization;
18
+ }
19
+ get providerName() {
20
+ return 'openai';
21
+ }
22
+ headers() {
23
+ if (!this.apiKey) {
24
+ throw new ConfigurationError('OPENAI_API_KEY not set (pass apiKey or set the env var)');
25
+ }
26
+ const headers = { Authorization: `Bearer ${this.apiKey}` };
27
+ if (this.organization)
28
+ headers['OpenAI-Organization'] = this.organization;
29
+ return headers;
30
+ }
31
+ async complete(request) {
32
+ const started = performance.now();
33
+ const payload = {
34
+ model: request.model,
35
+ messages: request.messages,
36
+ temperature: request.temperature,
37
+ max_tokens: request.maxTokens,
38
+ };
39
+ const data = await postJson(`${this.baseUrl}/chat/completions`, payload, this.headers(), this.timeoutSeconds);
40
+ const choices = data['choices'] ?? [];
41
+ const message = choices[0]?.['message'] ?? {};
42
+ const content = message['content'] ?? '';
43
+ const usageData = data['usage'] ?? {};
44
+ const usage = new TokenUsage(usageData['prompt_tokens'] ?? 0, usageData['completion_tokens'] ?? 0);
45
+ return this.buildResponse(request, content, usage, data['model'] ?? request.model, started);
46
+ }
47
+ async healthCheck() {
48
+ try {
49
+ this.headers();
50
+ return true;
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ }
56
+ }
57
+ /** Factory: build an OpenAI provider adapter. */
58
+ export function openaiAdapter(options = {}) {
59
+ return new OpenAIAdapter(options);
60
+ }
@@ -0,0 +1,36 @@
1
+ /** GavioRequest — the canonical, provider-agnostic request model. */
2
+ import type { Message, Provider } from './types.js';
3
+ export interface GavioRequestInit {
4
+ messages: Message[];
5
+ model: string;
6
+ provider: Provider | string;
7
+ traceId?: string;
8
+ agentId?: string | null;
9
+ parentTraceId?: string | null;
10
+ sessionId?: string | null;
11
+ options?: Record<string, unknown>;
12
+ metadata?: Record<string, unknown>;
13
+ }
14
+ /**
15
+ * A single gateway call. A `traceId` (UUID v7, time-sortable) is assigned
16
+ * automatically if not supplied. `parentTraceId` links calls into a
17
+ * multi-agent DAG.
18
+ */
19
+ export declare class GavioRequest {
20
+ messages: Message[];
21
+ model: string;
22
+ provider: Provider;
23
+ traceId: string;
24
+ agentId: string | null;
25
+ parentTraceId: string | null;
26
+ sessionId: string | null;
27
+ options: Record<string, unknown>;
28
+ metadata: Record<string, unknown>;
29
+ constructor(init: GavioRequestInit);
30
+ get temperature(): number;
31
+ get maxTokens(): number;
32
+ /** Concatenate message contents — used for hashing and token estimation. */
33
+ promptText(): string;
34
+ /** Return a shallow copy with replaced messages (interceptors mutate via this). */
35
+ copyWithMessages(messages: Message[]): GavioRequest;
36
+ }
@@ -0,0 +1,56 @@
1
+ /** GavioRequest — the canonical, provider-agnostic request model. */
2
+ import { newTraceId } from './ids.js';
3
+ import { coerceProvider } from './types.js';
4
+ /**
5
+ * A single gateway call. A `traceId` (UUID v7, time-sortable) is assigned
6
+ * automatically if not supplied. `parentTraceId` links calls into a
7
+ * multi-agent DAG.
8
+ */
9
+ export class GavioRequest {
10
+ messages;
11
+ model;
12
+ provider;
13
+ traceId;
14
+ agentId;
15
+ parentTraceId;
16
+ sessionId;
17
+ options;
18
+ metadata;
19
+ constructor(init) {
20
+ this.messages = init.messages;
21
+ this.model = init.model;
22
+ this.provider = coerceProvider(init.provider);
23
+ this.traceId = init.traceId ?? newTraceId();
24
+ this.agentId = init.agentId ?? null;
25
+ this.parentTraceId = init.parentTraceId ?? null;
26
+ this.sessionId = init.sessionId ?? null;
27
+ this.options = init.options ?? {};
28
+ this.metadata = init.metadata ?? {};
29
+ }
30
+ get temperature() {
31
+ const t = this.options['temperature'];
32
+ return typeof t === 'number' ? t : 0.7;
33
+ }
34
+ get maxTokens() {
35
+ const m = this.options['maxTokens'] ?? this.options['max_tokens'];
36
+ return typeof m === 'number' ? m : 1024;
37
+ }
38
+ /** Concatenate message contents — used for hashing and token estimation. */
39
+ promptText() {
40
+ return this.messages.map((m) => m.content ?? '').join('\n');
41
+ }
42
+ /** Return a shallow copy with replaced messages (interceptors mutate via this). */
43
+ copyWithMessages(messages) {
44
+ return new GavioRequest({
45
+ messages,
46
+ model: this.model,
47
+ provider: this.provider,
48
+ traceId: this.traceId,
49
+ agentId: this.agentId,
50
+ parentTraceId: this.parentTraceId,
51
+ sessionId: this.sessionId,
52
+ options: { ...this.options },
53
+ metadata: { ...this.metadata },
54
+ });
55
+ }
56
+ }
@@ -0,0 +1,38 @@
1
+ /** GavioResponse — the canonical response returned to the caller. */
2
+ import { TokenUsage } from './types.js';
3
+ import type { CacheType } from './types.js';
4
+ import type { AuditRecord } from './interceptors/audit/record.js';
5
+ export interface GavioResponseInit {
6
+ traceId: string;
7
+ content: string;
8
+ model: string;
9
+ provider: string;
10
+ modelVersion?: string;
11
+ usage?: TokenUsage;
12
+ costUsd?: number;
13
+ latencyMs?: number;
14
+ cacheHit?: boolean;
15
+ cacheType?: CacheType | null;
16
+ interceptorsFired?: string[];
17
+ audit?: AuditRecord | null;
18
+ metadata?: Record<string, unknown>;
19
+ }
20
+ /** Result of a gateway call, enriched by the post-interceptor pipeline. */
21
+ export declare class GavioResponse {
22
+ traceId: string;
23
+ content: string;
24
+ model: string;
25
+ provider: string;
26
+ modelVersion: string;
27
+ usage: TokenUsage;
28
+ costUsd: number;
29
+ latencyMs: number;
30
+ cacheHit: boolean;
31
+ cacheType: CacheType | null;
32
+ interceptorsFired: string[];
33
+ audit: AuditRecord | null;
34
+ metadata: Record<string, unknown>;
35
+ constructor(init: GavioResponseInit);
36
+ /** Return a copy with replaced content (used by PII restore, guardrails). */
37
+ copyWithContent(content: string): GavioResponse;
38
+ }
@@ -0,0 +1,51 @@
1
+ /** GavioResponse — the canonical response returned to the caller. */
2
+ import { TokenUsage } from './types.js';
3
+ /** Result of a gateway call, enriched by the post-interceptor pipeline. */
4
+ export class GavioResponse {
5
+ traceId;
6
+ content;
7
+ model;
8
+ provider;
9
+ modelVersion;
10
+ usage;
11
+ costUsd;
12
+ latencyMs;
13
+ cacheHit;
14
+ cacheType;
15
+ interceptorsFired;
16
+ audit;
17
+ metadata;
18
+ constructor(init) {
19
+ this.traceId = init.traceId;
20
+ this.content = init.content;
21
+ this.model = init.model;
22
+ this.provider = init.provider;
23
+ this.modelVersion = init.modelVersion ?? '';
24
+ this.usage = init.usage ?? new TokenUsage();
25
+ this.costUsd = init.costUsd ?? 0.0;
26
+ this.latencyMs = init.latencyMs ?? 0;
27
+ this.cacheHit = init.cacheHit ?? false;
28
+ this.cacheType = init.cacheType ?? null;
29
+ this.interceptorsFired = init.interceptorsFired ?? [];
30
+ this.audit = init.audit ?? null;
31
+ this.metadata = init.metadata ?? {};
32
+ }
33
+ /** Return a copy with replaced content (used by PII restore, guardrails). */
34
+ copyWithContent(content) {
35
+ return new GavioResponse({
36
+ traceId: this.traceId,
37
+ content,
38
+ model: this.model,
39
+ provider: this.provider,
40
+ modelVersion: this.modelVersion,
41
+ usage: this.usage,
42
+ costUsd: this.costUsd,
43
+ latencyMs: this.latencyMs,
44
+ cacheHit: this.cacheHit,
45
+ cacheType: this.cacheType,
46
+ interceptorsFired: [...this.interceptorsFired],
47
+ audit: this.audit,
48
+ metadata: { ...this.metadata },
49
+ });
50
+ }
51
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * GavioTestKit — run interceptor chains in isolation for unit tests.
3
+ *
4
+ * This v0.1.0 kit drives a chain against a {@link mockProvider} and lets you
5
+ * assert on PII detection, the redacted request, and the resulting audit record.
6
+ */
7
+ import { InterceptorContext } from '../context.js';
8
+ import type { Interceptor } from '../interceptors/base.js';
9
+ import type { AuditRecord } from '../interceptors/audit/record.js';
10
+ import type { ProviderAdapter } from '../providers/base.js';
11
+ import type { GavioResponse } from '../response.js';
12
+ import type { Message } from '../types.js';
13
+ export interface GavioTestKitOptions {
14
+ interceptors?: Interceptor[];
15
+ provider?: ProviderAdapter;
16
+ model?: string;
17
+ }
18
+ export interface RunResult {
19
+ response: GavioResponse;
20
+ ctx: InterceptorContext;
21
+ /** The request text as it reached the provider (post-redaction). */
22
+ preRequestText(): string;
23
+ /** True if any (or a specific) PII entity type was detected. */
24
+ piiDetected(entityType?: string): boolean;
25
+ /** The audit record produced for this call, if an audit interceptor ran. */
26
+ readonly auditRecord: AuditRecord | null;
27
+ }
28
+ export declare class GavioTestKit {
29
+ private readonly interceptors;
30
+ private readonly provider;
31
+ private readonly model;
32
+ constructor(options?: GavioTestKitOptions);
33
+ run(input: {
34
+ messages: Message[];
35
+ options?: Record<string, unknown>;
36
+ }): Promise<RunResult>;
37
+ }