@modular-prompt/driver 0.11.15 → 0.13.1

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 (122) hide show
  1. package/README.md +25 -0
  2. package/dist/anthropic/anthropic-driver.d.ts +38 -8
  3. package/dist/anthropic/anthropic-driver.d.ts.map +1 -1
  4. package/dist/anthropic/anthropic-driver.js +180 -164
  5. package/dist/anthropic/anthropic-driver.js.map +1 -1
  6. package/dist/cache-controller.d.ts +28 -0
  7. package/dist/cache-controller.d.ts.map +1 -0
  8. package/dist/cache-controller.js +2 -0
  9. package/dist/cache-controller.js.map +1 -0
  10. package/dist/cache-utils.d.ts +20 -0
  11. package/dist/cache-utils.d.ts.map +1 -0
  12. package/dist/cache-utils.js +71 -0
  13. package/dist/cache-utils.js.map +1 -0
  14. package/dist/content-utils.d.ts +9 -0
  15. package/dist/content-utils.d.ts.map +1 -1
  16. package/dist/content-utils.js +47 -0
  17. package/dist/content-utils.js.map +1 -1
  18. package/dist/driver-registry/config-based-factory.d.ts.map +1 -1
  19. package/dist/driver-registry/config-based-factory.js +7 -0
  20. package/dist/driver-registry/config-based-factory.js.map +1 -1
  21. package/dist/driver-registry/factory-helper.d.ts.map +1 -1
  22. package/dist/driver-registry/factory-helper.js +7 -4
  23. package/dist/driver-registry/factory-helper.js.map +1 -1
  24. package/dist/driver-registry/types.d.ts +6 -0
  25. package/dist/driver-registry/types.d.ts.map +1 -1
  26. package/dist/formatter/converter.js +1 -1
  27. package/dist/formatter/converter.js.map +1 -1
  28. package/dist/google-genai/element-converter.d.ts +11 -0
  29. package/dist/google-genai/element-converter.d.ts.map +1 -0
  30. package/dist/google-genai/element-converter.js +126 -0
  31. package/dist/google-genai/element-converter.js.map +1 -0
  32. package/dist/google-genai/google-genai-cache-controller.d.ts +24 -0
  33. package/dist/google-genai/google-genai-cache-controller.d.ts.map +1 -0
  34. package/dist/google-genai/google-genai-cache-controller.js +127 -0
  35. package/dist/google-genai/google-genai-cache-controller.js.map +1 -0
  36. package/dist/google-genai/google-genai-driver.d.ts +5 -29
  37. package/dist/google-genai/google-genai-driver.d.ts.map +1 -1
  38. package/dist/google-genai/google-genai-driver.js +92 -255
  39. package/dist/google-genai/google-genai-driver.js.map +1 -1
  40. package/dist/index.d.ts +4 -0
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +3 -0
  43. package/dist/index.js.map +1 -1
  44. package/dist/mlx-ml/mlx-cache-controller.d.ts +66 -0
  45. package/dist/mlx-ml/mlx-cache-controller.d.ts.map +1 -0
  46. package/dist/mlx-ml/mlx-cache-controller.js +600 -0
  47. package/dist/mlx-ml/mlx-cache-controller.js.map +1 -0
  48. package/dist/mlx-ml/mlx-driver.d.ts +13 -8
  49. package/dist/mlx-ml/mlx-driver.d.ts.map +1 -1
  50. package/dist/mlx-ml/mlx-driver.js +202 -143
  51. package/dist/mlx-ml/mlx-driver.js.map +1 -1
  52. package/dist/mlx-ml/mlx-message-utils.d.ts +9 -0
  53. package/dist/mlx-ml/mlx-message-utils.d.ts.map +1 -0
  54. package/dist/mlx-ml/mlx-message-utils.js +71 -0
  55. package/dist/mlx-ml/mlx-message-utils.js.map +1 -0
  56. package/dist/mlx-ml/process/harmony-parser.d.ts +3 -0
  57. package/dist/mlx-ml/process/harmony-parser.d.ts.map +1 -0
  58. package/dist/mlx-ml/process/harmony-parser.js +175 -0
  59. package/dist/mlx-ml/process/harmony-parser.js.map +1 -0
  60. package/dist/mlx-ml/process/index.d.ts +7 -3
  61. package/dist/mlx-ml/process/index.d.ts.map +1 -1
  62. package/dist/mlx-ml/process/index.js +22 -7
  63. package/dist/mlx-ml/process/index.js.map +1 -1
  64. package/dist/mlx-ml/process/model-handlers.d.ts +11 -58
  65. package/dist/mlx-ml/process/model-handlers.d.ts.map +1 -1
  66. package/dist/mlx-ml/process/model-handlers.js +29 -11
  67. package/dist/mlx-ml/process/model-handlers.js.map +1 -1
  68. package/dist/mlx-ml/process/model-specific.d.ts +7 -0
  69. package/dist/mlx-ml/process/model-specific.d.ts.map +1 -1
  70. package/dist/mlx-ml/process/model-specific.js +3 -0
  71. package/dist/mlx-ml/process/model-specific.js.map +1 -1
  72. package/dist/mlx-ml/process/parameter-validator.d.ts.map +1 -1
  73. package/dist/mlx-ml/process/parameter-validator.js +10 -3
  74. package/dist/mlx-ml/process/parameter-validator.js.map +1 -1
  75. package/dist/mlx-ml/process/process-communication.d.ts +3 -0
  76. package/dist/mlx-ml/process/process-communication.d.ts.map +1 -1
  77. package/dist/mlx-ml/process/process-communication.js +13 -0
  78. package/dist/mlx-ml/process/process-communication.js.map +1 -1
  79. package/dist/mlx-ml/process/queue.d.ts +5 -2
  80. package/dist/mlx-ml/process/queue.d.ts.map +1 -1
  81. package/dist/mlx-ml/process/queue.js +103 -15
  82. package/dist/mlx-ml/process/queue.js.map +1 -1
  83. package/dist/mlx-ml/process/response-processor.d.ts +18 -0
  84. package/dist/mlx-ml/process/response-processor.d.ts.map +1 -0
  85. package/dist/mlx-ml/process/response-processor.js +24 -0
  86. package/dist/mlx-ml/process/response-processor.js.map +1 -0
  87. package/dist/mlx-ml/process/types.d.ts +51 -4
  88. package/dist/mlx-ml/process/types.d.ts.map +1 -1
  89. package/dist/mlx-ml/tool-call-parser.d.ts.map +1 -1
  90. package/dist/mlx-ml/tool-call-parser.js +44 -68
  91. package/dist/mlx-ml/tool-call-parser.js.map +1 -1
  92. package/dist/mlx-ml/types.d.ts +1 -0
  93. package/dist/mlx-ml/types.d.ts.map +1 -1
  94. package/dist/openai/openai-driver.d.ts +0 -2
  95. package/dist/openai/openai-driver.d.ts.map +1 -1
  96. package/dist/openai/openai-driver.js.map +1 -1
  97. package/dist/types.d.ts +9 -0
  98. package/dist/types.d.ts.map +1 -1
  99. package/package.json +7 -4
  100. package/src/mlx-ml/python/__main__.py +41 -425
  101. package/src/mlx-ml/python/backends/__init__.py +3 -0
  102. package/src/mlx-ml/python/backends/base.py +84 -0
  103. package/src/mlx-ml/python/backends/mlx_lm.py +202 -0
  104. package/src/mlx-ml/python/backends/mlx_vlm.py +99 -0
  105. package/src/mlx-ml/python/examples/example_basic.py +93 -0
  106. package/src/mlx-ml/python/examples/example_tool_call.py +165 -0
  107. package/src/mlx-ml/python/handlers/__init__.py +6 -0
  108. package/src/mlx-ml/python/handlers/cache.py +81 -0
  109. package/src/mlx-ml/python/handlers/capabilities.py +6 -0
  110. package/src/mlx-ml/python/handlers/chat.py +221 -0
  111. package/src/mlx-ml/python/handlers/completion.py +36 -0
  112. package/src/mlx-ml/python/handlers/format_test.py +70 -0
  113. package/src/mlx-ml/python/handlers/tokenize.py +63 -0
  114. package/src/mlx-ml/python/pyproject.toml +15 -5
  115. package/src/mlx-ml/python/server.py +126 -0
  116. package/src/mlx-ml/python/tests/__init__.py +0 -0
  117. package/src/mlx-ml/python/utils/__init__.py +0 -0
  118. package/src/mlx-ml/python/utils/prompt_builder.py +54 -0
  119. package/src/mlx-ml/python/{token_utils.py → utils/token_utils.py} +13 -5
  120. package/src/mlx-ml/python/uv.lock +299 -57
  121. /package/src/mlx-ml/python/{chat_template_constraints.py → utils/chat_template_constraints.py} +0 -0
  122. /package/src/mlx-ml/python/{vlm_utils.py → utils/vlm_utils.py} +0 -0
@@ -0,0 +1,127 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { elementToPart, elementToContent, convertTools, mergeToolResultContents } from './element-converter.js';
3
+ function parseTtlSeconds(ttl) {
4
+ const match = ttl.match(/^(\d+)s$/);
5
+ if (!match) {
6
+ throw new Error(`Invalid TTL format "${ttl}": expected "<digits>s" (e.g. "3600s")`);
7
+ }
8
+ return Number(match[1]);
9
+ }
10
+ export class GoogleGenAICacheController {
11
+ client;
12
+ config;
13
+ managedCaches = new Set();
14
+ cacheByHash = new Map();
15
+ inflightRequests = new Map();
16
+ ttlSeconds;
17
+ normalizedTtl;
18
+ constructor(client, config) {
19
+ this.client = client;
20
+ this.config = config;
21
+ this.ttlSeconds = parseTtlSeconds(this.config?.ttl || '3600s');
22
+ this.normalizedTtl = `${this.ttlSeconds}s`;
23
+ }
24
+ computeCacheKey(params) {
25
+ const payload = { model: params.model };
26
+ if (params.instructions && params.instructions.length > 0) {
27
+ payload.systemInstruction = params.instructions.map(el => elementToPart(el));
28
+ }
29
+ if (params.data && params.data.length > 0) {
30
+ payload.contents = params.data.map(el => elementToContent(el));
31
+ }
32
+ if (params.tools && params.tools.length > 0) {
33
+ payload.tools = convertTools(params.tools);
34
+ }
35
+ return createHash('sha256').update(JSON.stringify(payload)).digest('hex');
36
+ }
37
+ sweepExpired() {
38
+ const now = Date.now();
39
+ for (const [key, entry] of this.cacheByHash) {
40
+ if ((now - entry.createdAt) / 1000 >= this.ttlSeconds) {
41
+ this.cacheByHash.delete(key);
42
+ this.managedCaches.delete(entry.handle.ref);
43
+ }
44
+ }
45
+ }
46
+ recordQuery() {
47
+ // no-op: GoogleGenAI cache controller does not track query stats
48
+ }
49
+ async prepare(params) {
50
+ this.sweepExpired();
51
+ const cacheKey = this.computeCacheKey(params);
52
+ const existing = this.cacheByHash.get(cacheKey);
53
+ if (existing) {
54
+ return existing.handle;
55
+ }
56
+ const inflight = this.inflightRequests.get(cacheKey);
57
+ if (inflight) {
58
+ return inflight;
59
+ }
60
+ const promise = this.createCache(params, cacheKey);
61
+ this.inflightRequests.set(cacheKey, promise);
62
+ try {
63
+ return await promise;
64
+ }
65
+ finally {
66
+ this.inflightRequests.delete(cacheKey);
67
+ }
68
+ }
69
+ async createCache(params, cacheKey) {
70
+ const cacheConfig = {
71
+ ttl: this.normalizedTtl,
72
+ displayName: this.config?.displayName,
73
+ };
74
+ if (params.instructions && params.instructions.length > 0) {
75
+ cacheConfig.systemInstruction = params.instructions.map(el => elementToPart(el));
76
+ }
77
+ if (params.data && params.data.length > 0) {
78
+ cacheConfig.contents = mergeToolResultContents(params.data.map(el => elementToContent(el)));
79
+ }
80
+ if (params.tools && params.tools.length > 0) {
81
+ cacheConfig.tools = convertTools(params.tools);
82
+ }
83
+ Object.keys(cacheConfig).forEach(key => {
84
+ if (cacheConfig[key] === undefined) {
85
+ delete cacheConfig[key];
86
+ }
87
+ });
88
+ const cache = await this.client.caches.create({
89
+ model: params.model,
90
+ config: cacheConfig,
91
+ });
92
+ if (!cache.name) {
93
+ throw new Error('GoogleGenAI caches.create() returned a cache without a name');
94
+ }
95
+ const ref = cache.name;
96
+ this.managedCaches.add(ref);
97
+ const handle = {
98
+ ref,
99
+ includes: {
100
+ instructions: (params.instructions?.length ?? 0) > 0,
101
+ dataElementCount: params.data?.length ?? 0,
102
+ tools: (params.tools?.length ?? 0) > 0,
103
+ },
104
+ };
105
+ this.cacheByHash.set(cacheKey, { handle, createdAt: Date.now() });
106
+ return handle;
107
+ }
108
+ async invalidate(handle) {
109
+ await this.client.caches.delete({ name: handle.ref });
110
+ this.managedCaches.delete(handle.ref);
111
+ for (const [key, entry] of this.cacheByHash) {
112
+ if (entry.handle.ref === handle.ref) {
113
+ this.cacheByHash.delete(key);
114
+ break;
115
+ }
116
+ }
117
+ }
118
+ async close() {
119
+ await Promise.allSettled([...this.inflightRequests.values()]);
120
+ this.inflightRequests.clear();
121
+ const deletions = [...this.managedCaches].map(name => this.client.caches.delete({ name }).catch(() => { }));
122
+ await Promise.all(deletions);
123
+ this.managedCaches.clear();
124
+ this.cacheByHash.clear();
125
+ }
126
+ }
127
+ //# sourceMappingURL=google-genai-cache-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-genai-cache-controller.js","sourceRoot":"","sources":["../../src/google-genai/google-genai-cache-controller.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import type { CompiledPrompt } from '@modular-prompt/core';
2
2
  import type { AIDriver, QueryOptions, QueryResult, StreamResult } from '../types.js';
3
+ import type { PromptCacheController } from '../cache-controller.js';
3
4
  /**
4
5
  * GoogleGenAI driver configuration
5
6
  */
@@ -8,6 +9,7 @@ export interface GoogleGenAIDriverConfig {
8
9
  model?: string;
9
10
  temperature?: number;
10
11
  defaultOptions?: Partial<GoogleGenAIQueryOptions>;
12
+ cacheController?: PromptCacheController;
11
13
  }
12
14
  /**
13
15
  * GoogleGenAI-specific query options
@@ -33,40 +35,14 @@ export declare class GoogleGenAIDriver implements AIDriver {
33
35
  private defaultTemperature;
34
36
  private _defaultOptions;
35
37
  private queryLogger;
38
+ private cacheController?;
36
39
  get defaultOptions(): Partial<GoogleGenAIQueryOptions>;
37
40
  set defaultOptions(value: Partial<GoogleGenAIQueryOptions>);
38
41
  constructor(config?: GoogleGenAIDriverConfig);
39
- /**
40
- * Convert Element to Part (flat text conversion)
41
- * Used for systemInstruction and simple data
42
- */
43
- private elementToPart;
44
- /**
45
- * Convert kind+value to GoogleGenAI functionResponse
46
- */
47
- private toFunctionResponsePayload;
48
- /**
49
- * Convert Element to Content (structure-preserving conversion)
50
- * Used for conversation history where role matters
51
- */
52
- private elementToContent;
53
- /**
54
- * Convert ChatMessage to GoogleGenAI Content format
55
- */
42
+ private buildPromptPayload;
43
+ private buildGenerationConfig;
56
44
  private chatMessageToContent;
57
- /**
58
- * Merge consecutive tool result Contents into a single user message.
59
- * Gemini API requires multiple functionResponses in one user message.
60
- */
61
- private mergeToolResultContents;
62
- /**
63
- * Convert JSON Schema to GoogleGenAI Schema format
64
- */
65
45
  private convertJsonSchema;
66
- /**
67
- * Convert ToolDefinition[] to GoogleGenAI tools format
68
- */
69
- private convertTools;
70
46
  /**
71
47
  * Convert ToolChoice to GoogleGenAI FunctionCallingConfig
72
48
  */
@@ -1 +1 @@
1
- {"version":3,"file":"google-genai-driver.d.ts","sourceRoot":"","sources":["../../src/google-genai/google-genai-driver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAW,MAAM,sBAAsB,CAAC;AACpE,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAqD,MAAM,aAAa,CAAC;AAKxI;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC,uBAAuB,CAAC,CAAC;CACnD;AAED;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,YAAY;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,cAAc,CAAC,EAAE;QACf,aAAa,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;KAC3C,CAAC;CACH;AAmBD;;GAEG;AACH,qBAAa,iBAAkB,YAAW,QAAQ;IAChD,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,eAAe,CAAmC;IAC1D,OAAO,CAAC,WAAW,CAAkC;IAErD,IAAI,cAAc,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAErD;IAED,IAAI,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,uBAAuB,CAAC,EAEzD;gBAEW,MAAM,GAAE,uBAA4B;IAahD;;;OAGG;IACH,OAAO,CAAC,aAAa;IAwDrB;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAcjC;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IA8CxB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAsC5B;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAmB/B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACH,OAAO,CAAC,YAAY;IASpB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAiBzB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAgBxB;;OAEG;IACG,KAAK,CACT,MAAM,EAAE,cAAc,EACtB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,WAAW,CAAC;IAoHvB;;OAEG;IACG,WAAW,CACf,MAAM,EAAE,cAAc,EACtB,OAAO,CAAC,EAAE,uBAAuB,GAChC,OAAO,CAAC,YAAY,CAAC;IAyKxB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
1
+ {"version":3,"file":"google-genai-driver.d.ts","sourceRoot":"","sources":["../../src/google-genai/google-genai-driver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAqC,MAAM,aAAa,CAAC;AAIxH,OAAO,KAAK,EAAE,qBAAqB,EAAe,MAAM,wBAAwB,CAAC;AAIjF;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAClD,eAAe,CAAC,EAAE,qBAAqB,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,YAAY;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,cAAc,CAAC,EAAE;QACf,aAAa,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;KAC3C,CAAC;CACH;AAmBD;;GAEG;AACH,qBAAa,iBAAkB,YAAW,QAAQ;IAChD,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,eAAe,CAAmC;IAC1D,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,eAAe,CAAC,CAAwB;IAEhD,IAAI,cAAc,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAErD;IAED,IAAI,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,uBAAuB,CAAC,EAEzD;gBAEW,MAAM,GAAE,uBAA4B;YAclC,kBAAkB;IAkDhC,OAAO,CAAC,qBAAqB;IAoD7B,OAAO,CAAC,oBAAoB;IAmC5B,OAAO,CAAC,iBAAiB;IAKzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAiBzB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAoBxB;;OAEG;IACG,KAAK,CACT,MAAM,EAAE,cAAc,EACtB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,WAAW,CAAC;IAsEvB;;OAEG;IACG,WAAW,CACf,MAAM,EAAE,cAAc,EACtB,OAAO,CAAC,EAAE,uBAAuB,GAChC,OAAO,CAAC,YAAY,CAAC;IA2HxB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
@@ -2,6 +2,8 @@ import { GoogleGenAI, FunctionCallingConfigMode } from '@google/genai';
2
2
  import { hasToolCalls, isToolResult } from '../types.js';
3
3
  import { contentToString } from '../content-utils.js';
4
4
  import { QueryLogger } from '../query-logger.js';
5
+ import { partitionPrompt } from '../cache-utils.js';
6
+ import { elementToPart, elementToContent, toFunctionResponsePayload, convertTools, mergeToolResultContents } from './element-converter.js';
5
7
  /**
6
8
  * Map finish reasons from GoogleGenAI to our format
7
9
  */
@@ -27,6 +29,7 @@ export class GoogleGenAIDriver {
27
29
  defaultTemperature;
28
30
  _defaultOptions;
29
31
  queryLogger = new QueryLogger('GoogleGenAI');
32
+ cacheController;
30
33
  get defaultOptions() {
31
34
  return this._defaultOptions;
32
35
  }
@@ -42,212 +45,126 @@ export class GoogleGenAIDriver {
42
45
  this.defaultModel = config.model || 'gemma-3-27b';
43
46
  this.defaultTemperature = config.temperature ?? 0.7;
44
47
  this._defaultOptions = config.defaultOptions || {};
48
+ this.cacheController = config.cacheController;
45
49
  }
46
- /**
47
- * Convert Element to Part (flat text conversion)
48
- * Used for systemInstruction and simple data
49
- */
50
- elementToPart(element) {
51
- if (typeof element === 'string') {
52
- return { text: element };
53
- }
54
- switch (element.type) {
55
- case 'text':
56
- return { text: element.content };
57
- case 'message': {
58
- // Flatten message as text
59
- if (element.role === 'tool') {
60
- // ToolResultMessageElement doesn't have content property
61
- const toolResultEl = element;
62
- const textValue = toolResultEl.kind === 'text' ? String(toolResultEl.value) : JSON.stringify(toolResultEl.value);
63
- return { text: `${element.role}: ${textValue}` };
64
- }
65
- const messageContent = contentToString(element.content);
66
- return { text: `${element.role}: ${messageContent}` };
67
- }
68
- case 'material': {
69
- const materialContent = contentToString(element.content);
70
- return { text: `# ${element.title}\n${materialContent}` };
71
- }
72
- case 'chunk': {
73
- const chunkContent = contentToString(element.content);
74
- const chunkHeader = element.index !== undefined && element.total !== undefined
75
- ? `[Chunk ${element.index + 1}/${element.total} of ${element.partOf}]`
76
- : `[Chunk of ${element.partOf}]`;
77
- return { text: `${chunkHeader}\n${chunkContent}` };
78
- }
79
- case 'section':
80
- case 'subsection': {
81
- // Section/SubSection elements should be compiled before reaching here
82
- // If they do reach here, flatten their items recursively
83
- const flattenItems = (items) => {
84
- return items.map(item => {
85
- if (typeof item === 'string')
86
- return item;
87
- if (typeof item === 'function')
88
- return ''; // DynamicContent should be resolved before this point
89
- return this.elementToPart(item).text || '';
90
- }).filter(Boolean).join('\n');
91
- };
92
- return { text: flattenItems(element.items) };
50
+ async buildPromptPayload(prompt, mergedOptions, model) {
51
+ if (this.cacheController) {
52
+ const partition = partitionPrompt(prompt);
53
+ const hasCacheableContent = partition.cacheable.instructions.length > 0 ||
54
+ partition.cacheable.data.length > 0 ||
55
+ (mergedOptions.tools && mergedOptions.tools.length > 0);
56
+ if (hasCacheableContent) {
57
+ const handle = await this.cacheController.prepare({
58
+ model,
59
+ instructions: partition.cacheable.instructions,
60
+ data: partition.cacheable.data,
61
+ tools: mergedOptions.tools,
62
+ });
63
+ const instructionsForRequest = handle.includes.instructions
64
+ ? partition.volatile.instructions
65
+ : [...partition.cacheable.instructions, ...partition.volatile.instructions];
66
+ const uncachedInstructionParts = instructionsForRequest.length > 0
67
+ ? instructionsForRequest.map(el => elementToPart(el))
68
+ : undefined;
69
+ const dataForRequest = handle.includes.dataElementCount >= partition.cacheable.data.length
70
+ ? partition.volatile.data
71
+ : [...partition.cacheable.data, ...partition.volatile.data];
72
+ const volatileElements = [...dataForRequest, ...partition.volatile.output];
73
+ const contents = volatileElements.length > 0
74
+ ? mergeToolResultContents(volatileElements.map(el => elementToContent(el)))
75
+ : [{ parts: [{ text: 'Please process according to the instructions.' }] }];
76
+ return { systemInstructionParts: uncachedInstructionParts, contents, cacheHandle: handle };
93
77
  }
94
- case 'json':
95
- return { text: typeof element.content === 'string' ? element.content : JSON.stringify(element.content, null, 2) };
96
- default:
97
- return { text: JSON.stringify(element) };
98
78
  }
79
+ const systemInstructionParts = prompt.instructions?.map(el => elementToPart(el));
80
+ const allDataElements = [...(prompt.data || []), ...(prompt.output || [])];
81
+ const contents = allDataElements.length > 0
82
+ ? mergeToolResultContents(allDataElements.map(el => elementToContent(el)))
83
+ : [{ parts: [{ text: 'Please process according to the instructions.' }] }];
84
+ return { systemInstructionParts, contents, cacheHandle: null };
99
85
  }
100
- /**
101
- * Convert kind+value to GoogleGenAI functionResponse
102
- */
103
- toFunctionResponsePayload(kind, value) {
104
- if (kind === 'text') {
105
- return { output: String(value) };
86
+ buildGenerationConfig(mergedOptions, cacheHandle, systemInstructionParts, prompt) {
87
+ const config = {
88
+ temperature: mergedOptions.temperature ?? this.defaultTemperature,
89
+ maxOutputTokens: mergedOptions.maxTokens,
90
+ topP: mergedOptions.topP,
91
+ topK: mergedOptions.topK,
92
+ candidateCount: mergedOptions.candidateCount,
93
+ stopSequences: mergedOptions.stopSequences,
94
+ thinkingConfig: mergedOptions.thinkingConfig ??
95
+ (mergedOptions.mode === 'thinking' ? { thinkingLevel: 'HIGH' } : undefined),
96
+ };
97
+ if (cacheHandle) {
98
+ config.cachedContent = cacheHandle.ref;
99
+ if (!cacheHandle.includes.tools && mergedOptions.tools && mergedOptions.tools.length > 0) {
100
+ config.tools = convertTools(mergedOptions.tools);
101
+ }
106
102
  }
107
- else if (kind === 'data') {
108
- // Check if value is a plain object
109
- if (value !== null && typeof value === 'object' && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype) {
110
- return value;
103
+ else {
104
+ if (mergedOptions.tools && mergedOptions.tools.length > 0) {
105
+ config.tools = convertTools(mergedOptions.tools);
111
106
  }
112
- return { output: value };
113
107
  }
114
- else { // 'error'
115
- return { error: value };
108
+ if (systemInstructionParts && systemInstructionParts.length > 0) {
109
+ config.systemInstruction = systemInstructionParts;
116
110
  }
117
- }
118
- /**
119
- * Convert Element to Content (structure-preserving conversion)
120
- * Used for conversation history where role matters
121
- */
122
- elementToContent(element) {
123
- if (typeof element === 'string') {
124
- return { parts: [{ text: element }] };
111
+ if (prompt.metadata?.outputSchema) {
112
+ config.responseMimeType = 'application/json';
113
+ config.responseSchema = this.convertJsonSchema(prompt.metadata.outputSchema);
125
114
  }
126
- if (element.type === 'message') {
127
- if (element.role === 'tool') {
128
- // ToolResultMessageElement
129
- const toolResultEl = element;
130
- return {
131
- role: 'user',
132
- parts: [{
133
- functionResponse: {
134
- name: toolResultEl.name,
135
- response: this.toFunctionResponsePayload(toolResultEl.kind, toolResultEl.value)
136
- }
137
- }]
138
- };
139
- }
140
- else if ('toolCalls' in element && element.toolCalls) {
141
- const parts = [];
142
- const content = contentToString(element.content);
143
- if (content)
144
- parts.push({ text: content });
145
- for (const tc of element.toolCalls) {
146
- parts.push({ functionCall: { name: tc.name, args: tc.arguments } });
147
- }
148
- return { role: 'model', parts };
149
- }
150
- else {
151
- // Role conversion:
152
- // - assistant → model
153
- // - system → user (Gemini API doesn't support system role in contents)
154
- // - user → user
155
- const role = element.role === 'assistant' ? 'model' : 'user';
156
- const messageContent = contentToString(element.content);
157
- return {
158
- role,
159
- parts: [{ text: messageContent }]
160
- };
161
- }
115
+ if (mergedOptions.toolChoice) {
116
+ config.toolConfig = {
117
+ functionCallingConfig: this.convertToolChoice(mergedOptions.toolChoice),
118
+ };
162
119
  }
163
- // Non-message elements: convert to Part and wrap in Content without role
164
- return {
165
- parts: [this.elementToPart(element)]
166
- };
120
+ Object.keys(config).forEach(key => {
121
+ if (config[key] === undefined) {
122
+ delete config[key];
123
+ }
124
+ });
125
+ return config;
167
126
  }
168
- /**
169
- * Convert ChatMessage to GoogleGenAI Content format
170
- */
171
127
  chatMessageToContent(message) {
172
128
  if (hasToolCalls(message)) {
173
- // AssistantToolCallMessage
174
129
  const parts = [];
175
130
  const textContent = contentToString(message.content);
176
131
  if (textContent) {
177
132
  parts.push({ text: textContent });
178
133
  }
179
134
  for (const tc of message.toolCalls) {
180
- parts.push({
181
- functionCall: {
182
- name: tc.name,
183
- args: tc.arguments
184
- }
185
- });
135
+ const part = {
136
+ functionCall: { name: tc.name, args: tc.arguments }
137
+ };
138
+ if (typeof tc.metadata?.thoughtSignature === 'string') {
139
+ part.thoughtSignature = tc.metadata.thoughtSignature;
140
+ }
141
+ parts.push(part);
186
142
  }
187
143
  return { role: 'model', parts };
188
144
  }
189
145
  else if (isToolResult(message)) {
190
- // ToolResultMessage
191
146
  return {
192
147
  role: 'user',
193
148
  parts: [{
194
149
  functionResponse: {
195
150
  name: message.name,
196
- response: this.toFunctionResponsePayload(message.kind, message.value)
151
+ response: toFunctionResponsePayload(message.kind, message.value)
197
152
  }
198
153
  }]
199
154
  };
200
155
  }
201
156
  else {
202
- // StandardChatMessage
203
157
  return {
204
158
  role: message.role === 'assistant' ? 'model' : 'user',
205
159
  parts: [{ text: contentToString(message.content) }]
206
160
  };
207
161
  }
208
162
  }
209
- /**
210
- * Merge consecutive tool result Contents into a single user message.
211
- * Gemini API requires multiple functionResponses in one user message.
212
- */
213
- mergeToolResultContents(contents) {
214
- const merged = [];
215
- for (const c of contents) {
216
- const prev = merged[merged.length - 1];
217
- if (prev &&
218
- c.role === 'user' &&
219
- prev.role === 'user' &&
220
- c.parts?.length === 1 && c.parts[0].functionResponse &&
221
- prev.parts && prev.parts.length > 0 && prev.parts[0].functionResponse) {
222
- prev.parts.push(c.parts[0]);
223
- }
224
- else {
225
- merged.push(c);
226
- }
227
- }
228
- return merged;
229
- }
230
- /**
231
- * Convert JSON Schema to GoogleGenAI Schema format
232
- */
233
163
  convertJsonSchema(schema) {
234
164
  if (!schema || typeof schema !== 'object')
235
165
  return undefined;
236
- // GoogleGenAI uses a specific schema format
237
- // For now, we'll pass it through and let the API handle it
238
166
  return schema;
239
167
  }
240
- /**
241
- * Convert ToolDefinition[] to GoogleGenAI tools format
242
- */
243
- convertTools(tools) {
244
- const functionDeclarations = tools.map(tool => ({
245
- name: tool.name,
246
- description: tool.description,
247
- parametersJsonSchema: tool.parameters,
248
- }));
249
- return [{ functionDeclarations }];
250
- }
251
168
  /**
252
169
  * Convert ToolChoice to GoogleGenAI FunctionCallingConfig
253
170
  */
@@ -277,11 +194,15 @@ export class GoogleGenAIDriver {
277
194
  for (const part of parts) {
278
195
  if (part.functionCall) {
279
196
  const fc = part.functionCall;
280
- toolCalls.push({
197
+ const toolCall = {
281
198
  id: fc.id || `call_${toolCalls.length}`,
282
199
  name: fc.name || '',
283
200
  arguments: fc.args ?? {},
284
- });
201
+ };
202
+ if (typeof part.thoughtSignature === 'string') {
203
+ toolCall.metadata = { thoughtSignature: part.thoughtSignature };
204
+ }
205
+ toolCalls.push(toolCall);
285
206
  }
286
207
  }
287
208
  return toolCalls;
@@ -294,52 +215,10 @@ export class GoogleGenAIDriver {
294
215
  // Merge options with defaults
295
216
  const mergedOptions = { ...this.defaultOptions, ...options };
296
217
  this.queryLogger.mark(mergedOptions);
297
- // Convert prompt to GoogleGenAI format
298
- // Instructions → systemInstruction (Part[])
299
- const systemInstructionParts = prompt.instructions?.map(el => this.elementToPart(el));
300
- // Data + Output → contents (Content[])
301
- const allDataElements = [...(prompt.data || []), ...(prompt.output || [])];
302
- const contents = allDataElements.length > 0
303
- ? this.mergeToolResultContents(allDataElements.map(el => this.elementToContent(el)))
304
- : [{ parts: [{ text: 'Please process according to the instructions.' }] }];
305
- // Create generation config
306
- const config = {
307
- temperature: mergedOptions.temperature ?? this.defaultTemperature,
308
- maxOutputTokens: mergedOptions.maxTokens,
309
- topP: mergedOptions.topP,
310
- topK: mergedOptions.topK,
311
- candidateCount: mergedOptions.candidateCount,
312
- stopSequences: mergedOptions.stopSequences,
313
- thinkingConfig: mergedOptions.thinkingConfig ??
314
- (mergedOptions.mode === 'thinking' ? { thinkingLevel: 'HIGH' } : undefined),
315
- };
316
- // Add system instruction if present
317
- if (systemInstructionParts && systemInstructionParts.length > 0) {
318
- config.systemInstruction = systemInstructionParts;
319
- }
320
- // Handle structured outputs
321
- if (prompt.metadata?.outputSchema) {
322
- config.responseMimeType = 'application/json';
323
- config.responseSchema = this.convertJsonSchema(prompt.metadata.outputSchema);
324
- }
325
- // Add tools configuration
326
- if (mergedOptions.tools && mergedOptions.tools.length > 0) {
327
- config.tools = this.convertTools(mergedOptions.tools);
328
- }
329
- if (mergedOptions.toolChoice) {
330
- config.toolConfig = {
331
- functionCallingConfig: this.convertToolChoice(mergedOptions.toolChoice),
332
- };
333
- }
334
- // Remove undefined values
335
- Object.keys(config).forEach(key => {
336
- if (config[key] === undefined) {
337
- delete config[key];
338
- }
339
- });
340
- // Get model name
341
218
  const model = mergedOptions.model || this.defaultModel;
342
- // Generate content
219
+ const { systemInstructionParts, contents, cacheHandle } = await this.buildPromptPayload(prompt, mergedOptions, model);
220
+ const config = this.buildGenerationConfig(mergedOptions, cacheHandle, systemInstructionParts, prompt);
221
+ this.cacheController?.recordQuery?.();
343
222
  const response = await this.client.models.generateContent({
344
223
  model,
345
224
  contents,
@@ -399,52 +278,10 @@ export class GoogleGenAIDriver {
399
278
  async streamQuery(prompt, options) {
400
279
  const mergedOptions = { ...this.defaultOptions, ...options };
401
280
  this.queryLogger.mark(mergedOptions);
402
- // Convert prompt to GoogleGenAI format
403
- // Instructions → systemInstruction (Part[])
404
- const systemInstructionParts = prompt.instructions?.map(el => this.elementToPart(el));
405
- // Data + Output → contents (Content[])
406
- const allDataElements = [...(prompt.data || []), ...(prompt.output || [])];
407
- const contents = allDataElements.length > 0
408
- ? allDataElements.map(el => this.elementToContent(el))
409
- : [{ parts: [{ text: 'Please process according to the instructions.' }] }];
410
- // Create generation config
411
- const config = {
412
- temperature: mergedOptions.temperature ?? this.defaultTemperature,
413
- maxOutputTokens: mergedOptions.maxTokens,
414
- topP: mergedOptions.topP,
415
- topK: mergedOptions.topK,
416
- candidateCount: mergedOptions.candidateCount,
417
- stopSequences: mergedOptions.stopSequences,
418
- thinkingConfig: mergedOptions.thinkingConfig ??
419
- (mergedOptions.mode === 'thinking' ? { thinkingLevel: 'HIGH' } : undefined),
420
- };
421
- // Add system instruction if present
422
- if (systemInstructionParts && systemInstructionParts.length > 0) {
423
- config.systemInstruction = systemInstructionParts;
424
- }
425
- // Handle structured outputs
426
- if (prompt.metadata?.outputSchema) {
427
- config.responseMimeType = 'application/json';
428
- config.responseSchema = this.convertJsonSchema(prompt.metadata.outputSchema);
429
- }
430
- // Add tools configuration
431
- if (mergedOptions.tools && mergedOptions.tools.length > 0) {
432
- config.tools = this.convertTools(mergedOptions.tools);
433
- }
434
- if (mergedOptions.toolChoice) {
435
- config.toolConfig = {
436
- functionCallingConfig: this.convertToolChoice(mergedOptions.toolChoice),
437
- };
438
- }
439
- // Remove undefined values
440
- Object.keys(config).forEach(key => {
441
- if (config[key] === undefined) {
442
- delete config[key];
443
- }
444
- });
445
- // Get model name
446
281
  const model = mergedOptions.model || this.defaultModel;
447
- // Generate content stream
282
+ const { systemInstructionParts, contents, cacheHandle } = await this.buildPromptPayload(prompt, mergedOptions, model);
283
+ const config = this.buildGenerationConfig(mergedOptions, cacheHandle, systemInstructionParts, prompt);
284
+ this.cacheController?.recordQuery?.();
448
285
  const streamResponse = await this.client.models.generateContentStream({
449
286
  model,
450
287
  contents,
@@ -550,7 +387,7 @@ export class GoogleGenAIDriver {
550
387
  * Close the client
551
388
  */
552
389
  async close() {
553
- // GoogleGenAI client doesn't need explicit closing
390
+ await this.cacheController?.close();
554
391
  }
555
392
  }
556
393
  //# sourceMappingURL=google-genai-driver.js.map