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,94 @@
1
+ /** openaiAdapter — Chat Completions API (GPT-4o, o1, ...). */
2
+
3
+ import { ConfigurationError } from '../errors.js'
4
+ import type { PricingProvider } from '../pricing.js'
5
+ import type { GavioRequest } from '../request.js'
6
+ import type { GavioResponse } from '../response.js'
7
+ import { TokenUsage } from '../types.js'
8
+ import { BaseProviderAdapter } from './base.js'
9
+ import { postJson } from './http.js'
10
+
11
+ const DEFAULT_BASE_URL = 'https://api.openai.com/v1'
12
+
13
+ export interface OpenAIAdapterOptions {
14
+ apiKey?: string
15
+ baseUrl?: string
16
+ timeoutMs?: number
17
+ organization?: string
18
+ pricing?: PricingProvider
19
+ }
20
+
21
+ class OpenAIAdapter extends BaseProviderAdapter {
22
+ private readonly apiKey: string | undefined
23
+ private readonly baseUrl: string
24
+ private readonly timeoutSeconds: number
25
+ private readonly organization: string | undefined
26
+
27
+ constructor(options: OpenAIAdapterOptions = {}) {
28
+ super(options.pricing)
29
+ this.apiKey = options.apiKey ?? process.env['OPENAI_API_KEY']
30
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, '')
31
+ this.timeoutSeconds = (options.timeoutMs ?? 30_000) / 1000
32
+ this.organization = options.organization
33
+ }
34
+
35
+ get providerName(): string {
36
+ return 'openai'
37
+ }
38
+
39
+ private headers(): Record<string, string> {
40
+ if (!this.apiKey) {
41
+ throw new ConfigurationError(
42
+ 'OPENAI_API_KEY not set (pass apiKey or set the env var)',
43
+ )
44
+ }
45
+ const headers: Record<string, string> = { Authorization: `Bearer ${this.apiKey}` }
46
+ if (this.organization) headers['OpenAI-Organization'] = this.organization
47
+ return headers
48
+ }
49
+
50
+ async complete(request: GavioRequest): Promise<GavioResponse> {
51
+ const started = performance.now()
52
+ const payload = {
53
+ model: request.model,
54
+ messages: request.messages,
55
+ temperature: request.temperature,
56
+ max_tokens: request.maxTokens,
57
+ }
58
+ const data = await postJson(
59
+ `${this.baseUrl}/chat/completions`,
60
+ payload,
61
+ this.headers(),
62
+ this.timeoutSeconds,
63
+ )
64
+ const choices = (data['choices'] as Array<Record<string, unknown>>) ?? []
65
+ const message = (choices[0]?.['message'] as Record<string, unknown>) ?? {}
66
+ const content = (message['content'] as string) ?? ''
67
+ const usageData = (data['usage'] as Record<string, number>) ?? {}
68
+ const usage = new TokenUsage(
69
+ usageData['prompt_tokens'] ?? 0,
70
+ usageData['completion_tokens'] ?? 0,
71
+ )
72
+ return this.buildResponse(
73
+ request,
74
+ content,
75
+ usage,
76
+ (data['model'] as string) ?? request.model,
77
+ started,
78
+ )
79
+ }
80
+
81
+ async healthCheck(): Promise<boolean> {
82
+ try {
83
+ this.headers()
84
+ return true
85
+ } catch {
86
+ return false
87
+ }
88
+ }
89
+ }
90
+
91
+ /** Factory: build an OpenAI provider adapter. */
92
+ export function openaiAdapter(options: OpenAIAdapterOptions = {}): OpenAIAdapter {
93
+ return new OpenAIAdapter(options)
94
+ }
package/src/request.ts ADDED
@@ -0,0 +1,76 @@
1
+ /** GavioRequest — the canonical, provider-agnostic request model. */
2
+
3
+ import { newTraceId } from './ids.js'
4
+ import { coerceProvider } from './types.js'
5
+ import type { Message, Provider } from './types.js'
6
+
7
+ export interface GavioRequestInit {
8
+ messages: Message[]
9
+ model: string
10
+ provider: Provider | string
11
+ traceId?: string
12
+ agentId?: string | null
13
+ parentTraceId?: string | null
14
+ sessionId?: string | null
15
+ options?: Record<string, unknown>
16
+ metadata?: Record<string, unknown>
17
+ }
18
+
19
+ /**
20
+ * A single gateway call. A `traceId` (UUID v7, time-sortable) is assigned
21
+ * automatically if not supplied. `parentTraceId` links calls into a
22
+ * multi-agent DAG.
23
+ */
24
+ export class GavioRequest {
25
+ messages: Message[]
26
+ model: string
27
+ provider: Provider
28
+ traceId: string
29
+ agentId: string | null
30
+ parentTraceId: string | null
31
+ sessionId: string | null
32
+ options: Record<string, unknown>
33
+ metadata: Record<string, unknown>
34
+
35
+ constructor(init: GavioRequestInit) {
36
+ this.messages = init.messages
37
+ this.model = init.model
38
+ this.provider = coerceProvider(init.provider)
39
+ this.traceId = init.traceId ?? newTraceId()
40
+ this.agentId = init.agentId ?? null
41
+ this.parentTraceId = init.parentTraceId ?? null
42
+ this.sessionId = init.sessionId ?? null
43
+ this.options = init.options ?? {}
44
+ this.metadata = init.metadata ?? {}
45
+ }
46
+
47
+ get temperature(): number {
48
+ const t = this.options['temperature']
49
+ return typeof t === 'number' ? t : 0.7
50
+ }
51
+
52
+ get maxTokens(): number {
53
+ const m = this.options['maxTokens'] ?? this.options['max_tokens']
54
+ return typeof m === 'number' ? m : 1024
55
+ }
56
+
57
+ /** Concatenate message contents — used for hashing and token estimation. */
58
+ promptText(): string {
59
+ return this.messages.map((m) => m.content ?? '').join('\n')
60
+ }
61
+
62
+ /** Return a shallow copy with replaced messages (interceptors mutate via this). */
63
+ copyWithMessages(messages: Message[]): GavioRequest {
64
+ return new GavioRequest({
65
+ messages,
66
+ model: this.model,
67
+ provider: this.provider,
68
+ traceId: this.traceId,
69
+ agentId: this.agentId,
70
+ parentTraceId: this.parentTraceId,
71
+ sessionId: this.sessionId,
72
+ options: { ...this.options },
73
+ metadata: { ...this.metadata },
74
+ })
75
+ }
76
+ }
@@ -0,0 +1,73 @@
1
+ /** GavioResponse — the canonical response returned to the caller. */
2
+
3
+ import { TokenUsage } from './types.js'
4
+ import type { CacheType } from './types.js'
5
+ import type { AuditRecord } from './interceptors/audit/record.js'
6
+
7
+ export interface GavioResponseInit {
8
+ traceId: string
9
+ content: string
10
+ model: string
11
+ provider: string
12
+ modelVersion?: string
13
+ usage?: TokenUsage
14
+ costUsd?: number
15
+ latencyMs?: number
16
+ cacheHit?: boolean
17
+ cacheType?: CacheType | null
18
+ interceptorsFired?: string[]
19
+ audit?: AuditRecord | null
20
+ metadata?: Record<string, unknown>
21
+ }
22
+
23
+ /** Result of a gateway call, enriched by the post-interceptor pipeline. */
24
+ export class GavioResponse {
25
+ traceId: string
26
+ content: string
27
+ model: string
28
+ provider: string
29
+ modelVersion: string
30
+ usage: TokenUsage
31
+ costUsd: number
32
+ latencyMs: number
33
+ cacheHit: boolean
34
+ cacheType: CacheType | null
35
+ interceptorsFired: string[]
36
+ audit: AuditRecord | null
37
+ metadata: Record<string, unknown>
38
+
39
+ constructor(init: GavioResponseInit) {
40
+ this.traceId = init.traceId
41
+ this.content = init.content
42
+ this.model = init.model
43
+ this.provider = init.provider
44
+ this.modelVersion = init.modelVersion ?? ''
45
+ this.usage = init.usage ?? new TokenUsage()
46
+ this.costUsd = init.costUsd ?? 0.0
47
+ this.latencyMs = init.latencyMs ?? 0
48
+ this.cacheHit = init.cacheHit ?? false
49
+ this.cacheType = init.cacheType ?? null
50
+ this.interceptorsFired = init.interceptorsFired ?? []
51
+ this.audit = init.audit ?? null
52
+ this.metadata = init.metadata ?? {}
53
+ }
54
+
55
+ /** Return a copy with replaced content (used by PII restore, guardrails). */
56
+ copyWithContent(content: string): GavioResponse {
57
+ return new GavioResponse({
58
+ traceId: this.traceId,
59
+ content,
60
+ model: this.model,
61
+ provider: this.provider,
62
+ modelVersion: this.modelVersion,
63
+ usage: this.usage,
64
+ costUsd: this.costUsd,
65
+ latencyMs: this.latencyMs,
66
+ cacheHit: this.cacheHit,
67
+ cacheType: this.cacheType,
68
+ interceptorsFired: [...this.interceptorsFired],
69
+ audit: this.audit,
70
+ metadata: { ...this.metadata },
71
+ })
72
+ }
73
+ }
@@ -0,0 +1,98 @@
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
+
8
+ import { InterceptorContext } from '../context.js'
9
+ import { isExecutorPolicy } from '../interceptors/base.js'
10
+ import type { Executor, ExecutorPolicy, Interceptor } from '../interceptors/base.js'
11
+ import { InterceptorChain } from '../interceptors/chain.js'
12
+ import type { AuditRecord } from '../interceptors/audit/record.js'
13
+ import { mockProvider } from '../providers/mock.js'
14
+ import type { ProviderAdapter } from '../providers/base.js'
15
+ import { GavioRequest } from '../request.js'
16
+ import type { GavioResponse } from '../response.js'
17
+ import { coerceProvider } from '../types.js'
18
+ import type { Message } from '../types.js'
19
+
20
+ /** Records the request as it reaches the provider (post-redaction). */
21
+ class CaptureInterceptor implements Interceptor {
22
+ readonly name = '_capture'
23
+ captured: GavioRequest | null = null
24
+
25
+ before(request: GavioRequest): GavioRequest {
26
+ this.captured = request
27
+ return request
28
+ }
29
+ }
30
+
31
+ export interface GavioTestKitOptions {
32
+ interceptors?: Interceptor[]
33
+ provider?: ProviderAdapter
34
+ model?: string
35
+ }
36
+
37
+ export interface RunResult {
38
+ response: GavioResponse
39
+ ctx: InterceptorContext
40
+ /** The request text as it reached the provider (post-redaction). */
41
+ preRequestText(): string
42
+ /** True if any (or a specific) PII entity type was detected. */
43
+ piiDetected(entityType?: string): boolean
44
+ /** The audit record produced for this call, if an audit interceptor ran. */
45
+ readonly auditRecord: AuditRecord | null
46
+ }
47
+
48
+ export class GavioTestKit {
49
+ private readonly interceptors: Interceptor[]
50
+ private readonly provider: ProviderAdapter
51
+ private readonly model: string
52
+
53
+ constructor(options: GavioTestKitOptions = {}) {
54
+ this.interceptors = [...(options.interceptors ?? [])]
55
+ this.provider = options.provider ?? mockProvider()
56
+ this.model = options.model ?? 'mock'
57
+ }
58
+
59
+ async run(input: { messages: Message[]; options?: Record<string, unknown> }): Promise<RunResult> {
60
+ const request = new GavioRequest({
61
+ messages: input.messages,
62
+ model: this.model,
63
+ provider: coerceProvider(this.provider.providerName),
64
+ options: input.options ?? {},
65
+ })
66
+ const ctx = new InterceptorContext({ traceId: request.traceId })
67
+
68
+ const capture = new CaptureInterceptor()
69
+ const all = [...this.interceptors, capture]
70
+ const policies = all.filter(isExecutorPolicy)
71
+ const regular = all.filter((i) => !isExecutorPolicy(i))
72
+ const chain = new InterceptorChain(regular)
73
+
74
+ let executor: Executor = (req) => this.provider.complete(req)
75
+ for (let i = policies.length - 1; i >= 0; i--) {
76
+ executor = wrap(policies[i]!, executor, ctx)
77
+ }
78
+
79
+ const response = await chain.execute(request, ctx, executor)
80
+
81
+ return {
82
+ response,
83
+ ctx,
84
+ preRequestText: () => capture.captured?.promptText() ?? '',
85
+ piiDetected: (entityType?: string): boolean => {
86
+ if (entityType === undefined) return ctx.piiEntityTypes.length > 0
87
+ return ctx.piiEntityTypes.includes(entityType)
88
+ },
89
+ get auditRecord(): AuditRecord | null {
90
+ return response.audit
91
+ },
92
+ }
93
+ }
94
+ }
95
+
96
+ function wrap(policy: ExecutorPolicy, inner: Executor, ctx: InterceptorContext): Executor {
97
+ return (req: GavioRequest) => policy.around(req, ctx, inner)
98
+ }
@@ -0,0 +1,6 @@
1
+ /** Gavio test utilities. */
2
+
3
+ export { GavioTestKit } from './harness.js'
4
+ export type { GavioTestKitOptions, RunResult } from './harness.js'
5
+ export { mockProvider } from '../providers/mock.js'
6
+ export type { MockProviderOptions } from '../providers/mock.js'
package/src/types.ts ADDED
@@ -0,0 +1,83 @@
1
+ /** Shared enums (as string unions / const objects) and utility types. */
2
+
3
+ /** A provider-agnostic chat message. */
4
+ export interface Message {
5
+ role: string
6
+ content: string
7
+ [key: string]: unknown
8
+ }
9
+
10
+ /** Supported LLM providers. String-valued for easy config + logging. */
11
+ export const Provider = {
12
+ OPENAI: 'openai',
13
+ ANTHROPIC: 'anthropic',
14
+ GEMINI: 'gemini',
15
+ AZURE_OPENAI: 'azure_openai',
16
+ OLLAMA: 'ollama',
17
+ BEDROCK: 'bedrock',
18
+ COHERE: 'cohere',
19
+ MOCK: 'mock',
20
+ } as const
21
+
22
+ export type Provider = (typeof Provider)[keyof typeof Provider]
23
+
24
+ /** Accept either a known provider value or any string and normalise to lowercase. */
25
+ export function coerceProvider(value: Provider | string): Provider {
26
+ return value.toLowerCase() as Provider
27
+ }
28
+
29
+ export const CacheType = {
30
+ EXACT: 'exact',
31
+ SEMANTIC: 'semantic',
32
+ } as const
33
+
34
+ export type CacheType = (typeof CacheType)[keyof typeof CacheType]
35
+
36
+ /** What PiiGuard does with a detected entity. */
37
+ export const PiiMode = {
38
+ REDACT: 'redact', // replace with a typed placeholder token
39
+ MASK: 'mask', // replace characters with asterisks
40
+ TAG: 'tag', // annotate inline but keep the value
41
+ BLOCK: 'block', // raise and refuse the request
42
+ } as const
43
+
44
+ export type PiiMode = (typeof PiiMode)[keyof typeof PiiMode]
45
+
46
+ export const Sensitivity = {
47
+ STRICT: 'strict',
48
+ BALANCED: 'balanced',
49
+ PERMISSIVE: 'permissive',
50
+ } as const
51
+
52
+ export type Sensitivity = (typeof Sensitivity)[keyof typeof Sensitivity]
53
+
54
+ export const GuardrailOutcome = {
55
+ PASS: 'PASS',
56
+ FAIL: 'FAIL',
57
+ HITL: 'HITL',
58
+ } as const
59
+
60
+ export type GuardrailOutcome = (typeof GuardrailOutcome)[keyof typeof GuardrailOutcome]
61
+
62
+ /** Token accounting for a single completion. */
63
+ export class TokenUsage {
64
+ readonly promptTokens: number
65
+ readonly completionTokens: number
66
+
67
+ constructor(promptTokens = 0, completionTokens = 0) {
68
+ this.promptTokens = promptTokens
69
+ this.completionTokens = completionTokens
70
+ }
71
+
72
+ get totalTokens(): number {
73
+ return this.promptTokens + this.completionTokens
74
+ }
75
+
76
+ toJSON(): { promptTokens: number; completionTokens: number; totalTokens: number } {
77
+ return {
78
+ promptTokens: this.promptTokens,
79
+ completionTokens: this.completionTokens,
80
+ totalTokens: this.totalTokens,
81
+ }
82
+ }
83
+ }