photon-grid-core 2.0.0 → 2.0.4

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 (175) hide show
  1. package/README.md +3 -3
  2. package/cell-selection/cell-selection-engine.d.ts.map +1 -1
  3. package/cell-selection/cell-selection-engine.js +11 -9
  4. package/cell-selection/cell-selection-engine.js.map +1 -1
  5. package/chart/range-chart-controller.d.ts +10 -0
  6. package/chart/range-chart-controller.d.ts.map +1 -1
  7. package/chart/range-chart-controller.js +22 -2
  8. package/chart/range-chart-controller.js.map +1 -1
  9. package/column-groups/column-group-drag-handler.d.ts.map +1 -1
  10. package/column-groups/column-group-drag-handler.js +64 -25
  11. package/column-groups/column-group-drag-handler.js.map +1 -1
  12. package/column-groups/column-group-header-builder.d.ts.map +1 -1
  13. package/column-groups/column-group-header-builder.js +7 -5
  14. package/column-groups/column-group-header-builder.js.map +1 -1
  15. package/column-groups/display-group-drag-handler.d.ts +7 -3
  16. package/column-groups/display-group-drag-handler.d.ts.map +1 -1
  17. package/column-groups/display-group-drag-handler.js +46 -7
  18. package/column-groups/display-group-drag-handler.js.map +1 -1
  19. package/column-groups/display-group-header-builder.d.ts +1 -1
  20. package/column-groups/display-group-header-builder.d.ts.map +1 -1
  21. package/column-groups/display-group-header-builder.js +9 -7
  22. package/column-groups/display-group-header-builder.js.map +1 -1
  23. package/core/column-model.d.ts +88 -5
  24. package/core/column-model.d.ts.map +1 -1
  25. package/core/column-model.js +215 -3
  26. package/core/column-model.js.map +1 -1
  27. package/core/create-grid.d.ts +54 -0
  28. package/core/create-grid.d.ts.map +1 -0
  29. package/core/create-grid.js +70 -0
  30. package/core/create-grid.js.map +1 -0
  31. package/core/grid-api.d.ts +10 -2
  32. package/core/grid-api.d.ts.map +1 -1
  33. package/core/grid-api.js +10 -0
  34. package/core/grid-api.js.map +1 -1
  35. package/core/grid-core.d.ts +7 -0
  36. package/core/grid-core.d.ts.map +1 -1
  37. package/core/grid-core.js +29 -8
  38. package/core/grid-core.js.map +1 -1
  39. package/core/grid-store.d.ts +5 -5
  40. package/core/grid-store.d.ts.map +1 -1
  41. package/core/pointer-utils.d.ts +59 -0
  42. package/core/pointer-utils.d.ts.map +1 -0
  43. package/core/pointer-utils.js +59 -0
  44. package/core/pointer-utils.js.map +1 -0
  45. package/drag-drop/drag-drop-engine.d.ts.map +1 -1
  46. package/drag-drop/drag-drop-engine.js +8 -8
  47. package/drag-drop/drag-drop-engine.js.map +1 -1
  48. package/engines/editing/cell-editor-engine.js +1 -1
  49. package/engines/editing/cell-editor-engine.js.map +1 -1
  50. package/index.d.ts +8 -3
  51. package/index.d.ts.map +1 -1
  52. package/index.js +4 -1
  53. package/index.js.map +1 -1
  54. package/package.json +1 -1
  55. package/photon-ai/index.d.ts +2 -0
  56. package/photon-ai/index.d.ts.map +1 -1
  57. package/photon-ai/index.js +2 -0
  58. package/photon-ai/index.js.map +1 -1
  59. package/photon-ai/photon-ai-panel.d.ts +40 -0
  60. package/photon-ai/photon-ai-panel.d.ts.map +1 -1
  61. package/photon-ai/photon-ai-panel.js +163 -8
  62. package/photon-ai/photon-ai-panel.js.map +1 -1
  63. package/photon-ai/photon-ai-service.d.ts +38 -1
  64. package/photon-ai/photon-ai-service.d.ts.map +1 -1
  65. package/photon-ai/photon-ai-service.js +72 -1
  66. package/photon-ai/photon-ai-service.js.map +1 -1
  67. package/photon-ai/provider/ai-provider.types.d.ts +174 -0
  68. package/photon-ai/provider/ai-provider.types.d.ts.map +1 -0
  69. package/photon-ai/provider/ai-provider.types.js +42 -0
  70. package/photon-ai/provider/ai-provider.types.js.map +1 -0
  71. package/photon-ai/provider/command-normalizer.d.ts +36 -0
  72. package/photon-ai/provider/command-normalizer.d.ts.map +1 -0
  73. package/photon-ai/provider/command-normalizer.js +120 -0
  74. package/photon-ai/provider/command-normalizer.js.map +1 -0
  75. package/photon-ai/provider/grid-context-builder.d.ts +37 -0
  76. package/photon-ai/provider/grid-context-builder.d.ts.map +1 -0
  77. package/photon-ai/provider/grid-context-builder.js +96 -0
  78. package/photon-ai/provider/grid-context-builder.js.map +1 -0
  79. package/photon-ai/provider/http-ai-provider.d.ts +42 -0
  80. package/photon-ai/provider/http-ai-provider.d.ts.map +1 -0
  81. package/photon-ai/provider/http-ai-provider.js +125 -0
  82. package/photon-ai/provider/http-ai-provider.js.map +1 -0
  83. package/photon-ai/provider/index.d.ts +30 -0
  84. package/photon-ai/provider/index.d.ts.map +1 -0
  85. package/photon-ai/provider/index.js +42 -0
  86. package/photon-ai/provider/index.js.map +1 -0
  87. package/photon-ai/provider/presets.d.ts +29 -0
  88. package/photon-ai/provider/presets.d.ts.map +1 -0
  89. package/photon-ai/provider/presets.js +110 -0
  90. package/photon-ai/provider/presets.js.map +1 -0
  91. package/photon-ai/provider/response-parsing.d.ts +22 -0
  92. package/photon-ai/provider/response-parsing.d.ts.map +1 -0
  93. package/photon-ai/provider/response-parsing.js +79 -0
  94. package/photon-ai/provider/response-parsing.js.map +1 -0
  95. package/photon-ai/provider/system-prompt.d.ts +26 -0
  96. package/photon-ai/provider/system-prompt.d.ts.map +1 -0
  97. package/photon-ai/provider/system-prompt.js +82 -0
  98. package/photon-ai/provider/system-prompt.js.map +1 -0
  99. package/photon-grid.min.js +497 -56
  100. package/renderer/body-renderer.d.ts +23 -0
  101. package/renderer/body-renderer.d.ts.map +1 -1
  102. package/renderer/body-renderer.js +102 -24
  103. package/renderer/body-renderer.js.map +1 -1
  104. package/renderer/column-chooser.d.ts +82 -0
  105. package/renderer/column-chooser.d.ts.map +1 -0
  106. package/renderer/column-chooser.js +335 -0
  107. package/renderer/column-chooser.js.map +1 -0
  108. package/renderer/column-menu.d.ts.map +1 -1
  109. package/renderer/column-menu.js +32 -20
  110. package/renderer/column-menu.js.map +1 -1
  111. package/renderer/column-style-manager.d.ts +27 -0
  112. package/renderer/column-style-manager.d.ts.map +1 -1
  113. package/renderer/column-style-manager.js +47 -0
  114. package/renderer/column-style-manager.js.map +1 -1
  115. package/renderer/detail-row-renderer.d.ts.map +1 -1
  116. package/renderer/detail-row-renderer.js +8 -6
  117. package/renderer/detail-row-renderer.js.map +1 -1
  118. package/renderer/grid-renderer.d.ts +10 -0
  119. package/renderer/grid-renderer.d.ts.map +1 -1
  120. package/renderer/grid-renderer.js +64 -20
  121. package/renderer/grid-renderer.js.map +1 -1
  122. package/renderer/group-drop-zone.js +14 -14
  123. package/renderer/group-drop-zone.js.map +1 -1
  124. package/renderer/header-renderer.d.ts +65 -0
  125. package/renderer/header-renderer.d.ts.map +1 -1
  126. package/renderer/header-renderer.js +347 -44
  127. package/renderer/header-renderer.js.map +1 -1
  128. package/renderer/row-animator.js +3 -3
  129. package/renderer/row-drag-renderer.js +6 -6
  130. package/renderer/row-drag-renderer.js.map +1 -1
  131. package/renderer/scroll-controller.d.ts +40 -0
  132. package/renderer/scroll-controller.d.ts.map +1 -1
  133. package/renderer/scroll-controller.js +184 -0
  134. package/renderer/scroll-controller.js.map +1 -1
  135. package/styles/base/column-chooser.css.d.ts +13 -0
  136. package/styles/base/column-chooser.css.d.ts.map +1 -0
  137. package/styles/base/column-chooser.css.js +241 -0
  138. package/styles/base/column-chooser.css.js.map +1 -0
  139. package/styles/base/column-context-menu.css.d.ts +1 -1
  140. package/styles/base/column-context-menu.css.d.ts.map +1 -1
  141. package/styles/base/column-context-menu.css.js +10 -0
  142. package/styles/base/column-context-menu.css.js.map +1 -1
  143. package/styles/base/filter.css.d.ts +1 -1
  144. package/styles/base/filter.css.js +1 -1
  145. package/styles/base/header.css.d.ts +1 -1
  146. package/styles/base/header.css.d.ts.map +1 -1
  147. package/styles/base/header.css.js +19 -0
  148. package/styles/base/header.css.js.map +1 -1
  149. package/styles/base/photon-ai.css.d.ts +1 -1
  150. package/styles/base/photon-ai.css.d.ts.map +1 -1
  151. package/styles/base/photon-ai.css.js +65 -0
  152. package/styles/base/photon-ai.css.js.map +1 -1
  153. package/styles/base/scrollbars.css.d.ts +1 -1
  154. package/styles/base/scrollbars.css.d.ts.map +1 -1
  155. package/styles/base/scrollbars.css.js +50 -4
  156. package/styles/base/scrollbars.css.js.map +1 -1
  157. package/styles/base/touch.css.d.ts +23 -0
  158. package/styles/base/touch.css.d.ts.map +1 -0
  159. package/styles/base/touch.css.js +85 -0
  160. package/styles/base/touch.css.js.map +1 -0
  161. package/styles/base-styles.d.ts.map +1 -1
  162. package/styles/base-styles.js +4 -0
  163. package/styles/base-styles.js.map +1 -1
  164. package/styles/themes/theme-dark.d.ts +1 -1
  165. package/styles/themes/theme-dark.d.ts.map +1 -1
  166. package/styles/themes/theme-dark.js +3 -0
  167. package/styles/themes/theme-dark.js.map +1 -1
  168. package/types/column.types.d.ts +36 -0
  169. package/types/column.types.d.ts.map +1 -1
  170. package/types/grid.types.d.ts +3 -2
  171. package/types/grid.types.d.ts.map +1 -1
  172. package/types/photon-ai.types.d.ts +125 -5
  173. package/types/photon-ai.types.d.ts.map +1 -1
  174. package/types/photon-ai.types.js +18 -1
  175. package/types/photon-ai.types.js.map +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grid-context-builder.d.ts","sourceRoot":"","sources":["../../../src/photon-ai/provider/grid-context-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,EAMV,iBAAiB,EAClB,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;GAYG;AACH,qBAAa,kBAAkB;IAE3B,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,uBAAuB;IAGpD,qEAAqE;IACrE,KAAK,IAAI,iBAAiB;IAQ1B,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,eAAe;IAiBvB,kGAAkG;IAClG,OAAO,CAAC,aAAa;IAMrB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,gBAAgB;CAMzB"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Distills the live grid into the compact {@link PhotonGridContext} a language
3
+ * model needs to interpret a command: which columns exist, which actions are
4
+ * possible, and what the grid currently looks like.
5
+ *
6
+ * Every piece is read from the *live* {@link GridApi} and
7
+ * {@link PhotonAICommandRegistry} at build time, so the model always reasons
8
+ * over the grid's current shape — including any custom intents a host app
9
+ * registered at runtime (they appear in {@link buildCapabilities} automatically
10
+ * because it reads the registry, never a hard-coded list). This is what keeps
11
+ * the generative back-end in lock-step with the deterministic one: both are
12
+ * driven by the same registry.
13
+ */
14
+ export class GridContextBuilder {
15
+ constructor(api, registry) {
16
+ this.api = api;
17
+ this.registry = registry;
18
+ }
19
+ /** Builds a fresh snapshot. Cheap enough to call once per prompt. */
20
+ build() {
21
+ return {
22
+ columns: this.buildColumns(),
23
+ capabilities: this.buildCapabilities(),
24
+ state: this.buildState(),
25
+ };
26
+ }
27
+ buildColumns() {
28
+ return this.api.getAllColumns().map((col) => this.toColumnContext(col));
29
+ }
30
+ toColumnContext(col) {
31
+ const options = this.columnOptions(col);
32
+ return {
33
+ colId: col.colId,
34
+ header: col.header,
35
+ field: col.field,
36
+ type: col.type,
37
+ // These flags default to enabled in the grid, so treat "unset" as `true`.
38
+ sortable: col.sortable !== false,
39
+ filterable: col.filterable !== false,
40
+ groupable: col.groupable !== false,
41
+ pinned: col.pinned ?? null,
42
+ visible: col.visible !== false,
43
+ ...(options ? { options } : {}),
44
+ };
45
+ }
46
+ /** Enumerated values a `dropdown`/`enum` column accepts, so the model filters by a real value. */
47
+ columnOptions(col) {
48
+ if (col.dropdownOptions?.length)
49
+ return col.dropdownOptions.map((o) => o.value);
50
+ if (col.enumOptions?.length)
51
+ return col.enumOptions;
52
+ return undefined;
53
+ }
54
+ /**
55
+ * The catalog of actions the model may emit — one entry per registered
56
+ * intent. Sourced from the registry so it never drifts from what
57
+ * `CommandExecutor` can actually run.
58
+ */
59
+ buildCapabilities() {
60
+ return this.registry
61
+ .getAll()
62
+ .map((intent) => ({ type: intent.key, description: intent.description ?? intent.key }));
63
+ }
64
+ buildState() {
65
+ return {
66
+ totalRowCount: this.api.getAllRows().filter((r) => r.type === 'data').length,
67
+ visibleRowCount: this.api.getVisibleRows().filter((r) => r.type === 'data').length,
68
+ sort: this.buildSortState(),
69
+ filters: this.buildFilterState(),
70
+ groupedColumns: [...(this.api.getGridState().groupedColumns ?? [])],
71
+ selectedRowCount: this.api.getSelectedCount(),
72
+ };
73
+ }
74
+ buildSortState() {
75
+ return this.api.getSortConfig().map((s) => ({ colId: s.colId, order: s.order }));
76
+ }
77
+ buildFilterState() {
78
+ return Object.values(this.api.getFilterModel()).map((filter) => ({
79
+ colId: filter.colId,
80
+ summary: summarizeFilter(filter),
81
+ }));
82
+ }
83
+ }
84
+ /** Compresses a column filter into a single human/model-readable phrase. */
85
+ function summarizeFilter(filter) {
86
+ if (filter.selectedIds?.length)
87
+ return `in [${filter.selectedIds.join(', ')}]`;
88
+ const condition = filter.conditions[0];
89
+ if (!condition)
90
+ return 'active';
91
+ if (condition.operator === 'blank' || condition.operator === 'notBlank')
92
+ return condition.operator;
93
+ const to = condition.valueTo !== undefined ? `..${String(condition.valueTo)}` : '';
94
+ return `${condition.operator} ${String(condition.value)}${to}`;
95
+ }
96
+ //# sourceMappingURL=grid-context-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grid-context-builder.js","sourceRoot":"","sources":["../../../src/photon-ai/provider/grid-context-builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type { PhotonAIProviderConfig } from '../../types/photon-ai.types';
2
+ import { type PhotonAIProvider, type PhotonAIProviderRequest, type PhotonAIResponse } from './ai-provider.types';
3
+ /**
4
+ * The single, provider-agnostic {@link PhotonAIProvider}. It is driven entirely
5
+ * by a consumer-supplied {@link PhotonAIProviderConfig}: pick a hosted
6
+ * {@link PhotonAIProviderType} for out-of-the-box defaults (endpoint, model,
7
+ * auth, wire format), or override any field — up to and including the full
8
+ * request/response wire format via `requestTransformer`/`responseTransformer` —
9
+ * to talk to any JSON HTTP model endpoint.
10
+ *
11
+ * Uses the native `fetch` API only (no SDK), so it runs in every browser Photon
12
+ * Grid supports. The API key is read from config at request time and applied
13
+ * with the provider's auth scheme; it is never hard-coded, cached, or logged.
14
+ * Every failure mode — HTTP errors, rate limits, auth problems, safety blocks,
15
+ * timeouts, and malformed JSON — surfaces as a categorized
16
+ * {@link PhotonAIProviderError}.
17
+ */
18
+ export declare class HttpAIProvider implements PhotonAIProvider {
19
+ private readonly type;
20
+ private readonly preset;
21
+ private readonly apiKey;
22
+ private readonly apiUrl;
23
+ private readonly model;
24
+ private readonly headers;
25
+ private readonly temperature;
26
+ private readonly timeoutMs;
27
+ private readonly transformRequest;
28
+ private readonly transformResponse;
29
+ constructor(config: PhotonAIProviderConfig);
30
+ generate(request: PhotonAIProviderRequest): Promise<PhotonAIResponse>;
31
+ /** Resolves the final URL (auth-in-query for Gemini, plain otherwise). */
32
+ private buildUrl;
33
+ /** Merges Content-Type + provider auth headers + consumer headers (consumer wins). */
34
+ private buildHeaders;
35
+ /** POSTs the request as JSON, enforcing the configured timeout via `AbortController`. */
36
+ private fetchWithTimeout;
37
+ /** Validates the HTTP status, mapping known failure statuses (auth, rate-limit) to categorized errors, and returns the parsed JSON body. */
38
+ private parseHttpResponse;
39
+ /** Best-effort extraction of a provider error message from a non-2xx body (`{ error: { message } }` or `{ error: "..." }`). */
40
+ private readErrorMessage;
41
+ }
42
+ //# sourceMappingURL=http-ai-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-ai-provider.d.ts","sourceRoot":"","sources":["../../../src/photon-ai/provider/http-ai-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAG1E,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAE5B,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAC;AAK7B;;;;;;;;;;;;;;GAcG;AACH,qBAAa,cAAe,YAAW,gBAAgB;IACrD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAuB;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAwC;IACzE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA0C;gBAEhE,MAAM,EAAE,sBAAsB;IA2BpC,QAAQ,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAkB3E,0EAA0E;IAC1E,OAAO,CAAC,QAAQ;IAKhB,sFAAsF;IACtF,OAAO,CAAC,YAAY;IAKpB,yFAAyF;YAC3E,gBAAgB;IA+B9B,4IAA4I;YAC9H,iBAAiB;IAoC/B,+HAA+H;YACjH,gBAAgB;CAS/B"}
@@ -0,0 +1,125 @@
1
+ import { PhotonAIProviderType } from '../../types/photon-ai.types';
2
+ import { getPreset } from './presets';
3
+ import { PhotonAIErrorKind, PhotonAIProviderError, } from './ai-provider.types';
4
+ /** Default request deadline. */
5
+ const DEFAULT_TIMEOUT_MS = 30000;
6
+ /**
7
+ * The single, provider-agnostic {@link PhotonAIProvider}. It is driven entirely
8
+ * by a consumer-supplied {@link PhotonAIProviderConfig}: pick a hosted
9
+ * {@link PhotonAIProviderType} for out-of-the-box defaults (endpoint, model,
10
+ * auth, wire format), or override any field — up to and including the full
11
+ * request/response wire format via `requestTransformer`/`responseTransformer` —
12
+ * to talk to any JSON HTTP model endpoint.
13
+ *
14
+ * Uses the native `fetch` API only (no SDK), so it runs in every browser Photon
15
+ * Grid supports. The API key is read from config at request time and applied
16
+ * with the provider's auth scheme; it is never hard-coded, cached, or logged.
17
+ * Every failure mode — HTTP errors, rate limits, auth problems, safety blocks,
18
+ * timeouts, and malformed JSON — surfaces as a categorized
19
+ * {@link PhotonAIProviderError}.
20
+ */
21
+ export class HttpAIProvider {
22
+ constructor(config) {
23
+ this.type = config.type ?? PhotonAIProviderType.Gemini;
24
+ this.preset = getPreset(this.type);
25
+ // A Custom provider ships no defaults — it must be fully specified.
26
+ if (!this.preset) {
27
+ if (!config.apiUrl || !config.requestTransformer || !config.responseTransformer) {
28
+ throw new PhotonAIProviderError(PhotonAIErrorKind.Http, 'A custom Photon AI provider requires apiUrl, requestTransformer, and responseTransformer.');
29
+ }
30
+ }
31
+ this.apiKey = config.apiKey?.trim() || undefined;
32
+ this.apiUrl = config.apiUrl?.trim() || this.preset?.defaultApiUrl || '';
33
+ this.model = config.model?.trim() || this.preset?.defaultModel || '';
34
+ this.headers = { ...(config.headers ?? {}) };
35
+ this.temperature = config.temperature ?? 0;
36
+ this.timeoutMs = config.requestTimeoutMs ?? DEFAULT_TIMEOUT_MS;
37
+ // Consumer transformers win; otherwise fall back to the preset's. The
38
+ // Custom-provider guard above guarantees both exist when there's no preset.
39
+ this.transformRequest = config.requestTransformer ?? this.preset.transformRequest;
40
+ this.transformResponse = config.responseTransformer ?? this.preset.transformResponse;
41
+ }
42
+ async generate(request) {
43
+ const neutral = {
44
+ systemInstruction: request.systemInstruction,
45
+ userCommand: request.userCommand,
46
+ gridContext: request.gridContext,
47
+ model: this.model,
48
+ temperature: this.temperature,
49
+ };
50
+ const body = this.transformRequest(neutral);
51
+ const url = this.buildUrl();
52
+ const headers = this.buildHeaders();
53
+ const response = await this.fetchWithTimeout(url, headers, body, request.signal);
54
+ const payload = await this.parseHttpResponse(response);
55
+ return this.transformResponse(payload);
56
+ }
57
+ /** Resolves the final URL (auth-in-query for Gemini, plain otherwise). */
58
+ buildUrl() {
59
+ if (this.preset)
60
+ return this.preset.buildUrl(this.apiUrl, this.model, this.apiKey);
61
+ return this.apiUrl;
62
+ }
63
+ /** Merges Content-Type + provider auth headers + consumer headers (consumer wins). */
64
+ buildHeaders() {
65
+ const auth = this.preset ? this.preset.buildHeaders(this.apiKey) : {};
66
+ return { 'Content-Type': 'application/json', ...auth, ...this.headers };
67
+ }
68
+ /** POSTs the request as JSON, enforcing the configured timeout via `AbortController`. */
69
+ async fetchWithTimeout(url, headers, body, external) {
70
+ const controller = new AbortController();
71
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
72
+ const onExternalAbort = () => controller.abort();
73
+ external?.addEventListener('abort', onExternalAbort, { once: true });
74
+ try {
75
+ return await fetch(url, { method: 'POST', headers, body: JSON.stringify(body), signal: controller.signal });
76
+ }
77
+ catch (err) {
78
+ if (external?.aborted) {
79
+ throw new PhotonAIProviderError(PhotonAIErrorKind.Network, 'Photon AI request was cancelled.');
80
+ }
81
+ if (controller.signal.aborted) {
82
+ throw new PhotonAIProviderError(PhotonAIErrorKind.Timeout, `The model did not respond within ${this.timeoutMs / 1000}s. Please try again.`);
83
+ }
84
+ const detail = err instanceof Error ? err.message : String(err);
85
+ throw new PhotonAIProviderError(PhotonAIErrorKind.Network, `Couldn't reach the model provider: ${detail}`);
86
+ }
87
+ finally {
88
+ clearTimeout(timer);
89
+ external?.removeEventListener('abort', onExternalAbort);
90
+ }
91
+ }
92
+ /** Validates the HTTP status, mapping known failure statuses (auth, rate-limit) to categorized errors, and returns the parsed JSON body. */
93
+ async parseHttpResponse(response) {
94
+ if (response.ok) {
95
+ try {
96
+ return await response.json();
97
+ }
98
+ catch {
99
+ throw new PhotonAIProviderError(PhotonAIErrorKind.InvalidResponse, 'The model provider returned a response that was not valid JSON.');
100
+ }
101
+ }
102
+ const message = await this.readErrorMessage(response);
103
+ const suffix = message ? ` ${message}` : '';
104
+ if (response.status === 429) {
105
+ throw new PhotonAIProviderError(PhotonAIErrorKind.RateLimit, `Rate limit reached. Please wait a moment and try again.${suffix}`, 429);
106
+ }
107
+ if (response.status === 401 || response.status === 403) {
108
+ throw new PhotonAIProviderError(PhotonAIErrorKind.Auth, `The model provider rejected the API key (HTTP ${response.status}).${suffix}`, response.status);
109
+ }
110
+ throw new PhotonAIProviderError(PhotonAIErrorKind.Http, `The model provider request failed (HTTP ${response.status}).${suffix}`, response.status);
111
+ }
112
+ /** Best-effort extraction of a provider error message from a non-2xx body (`{ error: { message } }` or `{ error: "..." }`). */
113
+ async readErrorMessage(response) {
114
+ try {
115
+ const payload = (await response.json());
116
+ if (typeof payload.error === 'string')
117
+ return payload.error.trim();
118
+ return payload.error?.message?.trim() ?? '';
119
+ }
120
+ catch {
121
+ return '';
122
+ }
123
+ }
124
+ }
125
+ //# sourceMappingURL=http-ai-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-ai-provider.js","sourceRoot":"","sources":["../../../src/photon-ai/provider/http-ai-provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { PhotonAIProviderConfig } from '../../types/photon-ai.types';
2
+ import type { PhotonAIProvider } from './ai-provider.types';
3
+ export type { PhotonAIProvider, PhotonAIProviderRequest, PhotonAIRequest, PhotonAIResponse, PhotonAIGeneration, PhotonAIAction, PhotonGridContext, PhotonAIColumnContext, PhotonAICapability, PhotonAIGridState, PhotonAISortState, PhotonAIFilterState, } from './ai-provider.types';
4
+ export { PhotonAIErrorKind, PhotonAIProviderError, describeProviderError } from './ai-provider.types';
5
+ export { HttpAIProvider } from './http-ai-provider';
6
+ export { GridContextBuilder } from './grid-context-builder';
7
+ export { CommandNormalizer } from './command-normalizer';
8
+ export { buildSystemInstruction, serializeGridContext } from './system-prompt';
9
+ export { getPreset, coerceGeneration, parseGenerationText, type ProviderPreset } from './presets';
10
+ export { buildUserPromptText, normalizeActions } from './response-parsing';
11
+ /**
12
+ * Instantiates the {@link PhotonAIProvider} described by a
13
+ * {@link PhotonAIProviderConfig}. Returns `null` when no *usable* provider can
14
+ * be built — this is the signal for
15
+ * {@link import('../photon-ai-service').PhotonAIService} to stay in its default
16
+ * deterministic mode rather than fail. Configuring a provider is always
17
+ * strictly additive: a missing key or an incomplete custom setup silently
18
+ * keeps the offline pipeline.
19
+ *
20
+ * "Usable" means:
21
+ * - a hosted provider (Gemini/OpenAI/Anthropic) with an API key, or auth
22
+ * supplied via `headers`; or
23
+ * - a {@link PhotonAIProviderType.Custom} provider with an endpoint and both
24
+ * transformers.
25
+ *
26
+ * Every provider type routes through the single {@link HttpAIProvider}; adding a
27
+ * new hosted provider is just a new preset, not a new class.
28
+ */
29
+ export declare function createAIProvider(config: PhotonAIProviderConfig | undefined): PhotonAIProvider | null;
30
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/photon-ai/provider/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAG1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,YAAY,EACV,gBAAgB,EAChB,uBAAuB,EACvB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACtG,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAClG,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3E;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,SAAS,GAAG,gBAAgB,GAAG,IAAI,CAapG"}
@@ -0,0 +1,42 @@
1
+ import { PhotonAIProviderType } from '../../types/photon-ai.types';
2
+ import { HttpAIProvider } from './http-ai-provider';
3
+ export { PhotonAIErrorKind, PhotonAIProviderError, describeProviderError } from './ai-provider.types';
4
+ export { HttpAIProvider } from './http-ai-provider';
5
+ export { GridContextBuilder } from './grid-context-builder';
6
+ export { CommandNormalizer } from './command-normalizer';
7
+ export { buildSystemInstruction, serializeGridContext } from './system-prompt';
8
+ export { getPreset, coerceGeneration, parseGenerationText } from './presets';
9
+ export { buildUserPromptText, normalizeActions } from './response-parsing';
10
+ /**
11
+ * Instantiates the {@link PhotonAIProvider} described by a
12
+ * {@link PhotonAIProviderConfig}. Returns `null` when no *usable* provider can
13
+ * be built — this is the signal for
14
+ * {@link import('../photon-ai-service').PhotonAIService} to stay in its default
15
+ * deterministic mode rather than fail. Configuring a provider is always
16
+ * strictly additive: a missing key or an incomplete custom setup silently
17
+ * keeps the offline pipeline.
18
+ *
19
+ * "Usable" means:
20
+ * - a hosted provider (Gemini/OpenAI/Anthropic) with an API key, or auth
21
+ * supplied via `headers`; or
22
+ * - a {@link PhotonAIProviderType.Custom} provider with an endpoint and both
23
+ * transformers.
24
+ *
25
+ * Every provider type routes through the single {@link HttpAIProvider}; adding a
26
+ * new hosted provider is just a new preset, not a new class.
27
+ */
28
+ export function createAIProvider(config) {
29
+ if (!config)
30
+ return null;
31
+ const type = config.type ?? PhotonAIProviderType.Gemini;
32
+ if (type === PhotonAIProviderType.Custom) {
33
+ if (!config.apiUrl || !config.requestTransformer || !config.responseTransformer)
34
+ return null;
35
+ }
36
+ else if (!config.apiKey?.trim() && !config.headers) {
37
+ // A hosted provider with no way to authenticate — fall back to deterministic.
38
+ return null;
39
+ }
40
+ return new HttpAIProvider(config);
41
+ }
42
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/photon-ai/provider/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAiBpD,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACtG,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,mBAAmB,EAAuB,MAAM,WAAW,CAAC;AAClG,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAA0C;IACzE,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,oBAAoB,CAAC,MAAM,CAAC;IAExD,IAAI,IAAI,KAAK,oBAAoB,CAAC,MAAM,EAAE,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,kBAAkB,IAAI,CAAC,MAAM,CAAC,mBAAmB;YAAE,OAAO,IAAI,CAAC;IAC/F,CAAC;SAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACrD,8EAA8E;QAC9E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { PhotonAIProviderType } from '../../types/photon-ai.types';
2
+ import { type PhotonAIRequest, type PhotonAIResponse } from './ai-provider.types';
3
+ import { coerceGeneration, parseGenerationText } from './response-parsing';
4
+ /**
5
+ * A built-in provider preset: everything Photon AI needs to talk to a hosted
6
+ * LLM out of the box — default endpoint + model, how to place auth, and the
7
+ * request/response wire transformers. A consumer overrides any of these
8
+ * through {@link import('../../types/photon-ai.types').PhotonAIProviderConfig},
9
+ * so a preset is only the *default* wiring, never a hard requirement.
10
+ */
11
+ export interface ProviderPreset {
12
+ /** Default REST endpoint. May contain a `{model}` placeholder (Gemini). */
13
+ readonly defaultApiUrl: string;
14
+ /** Default model id for this provider. */
15
+ readonly defaultModel: string;
16
+ /** Builds the final request URL from the resolved endpoint, model, and key. */
17
+ buildUrl(apiUrl: string, model: string, apiKey: string | undefined): string;
18
+ /** Builds provider auth/headers from the key. Merged *under* consumer `headers`. */
19
+ buildHeaders(apiKey: string | undefined): Record<string, string>;
20
+ /** Default request-body transformer (overridable via config). */
21
+ transformRequest(request: PhotonAIRequest): unknown;
22
+ /** Default response parser (overridable via config). */
23
+ transformResponse(response: unknown): PhotonAIResponse;
24
+ }
25
+ /** The preset for a provider type, or `null` for {@link PhotonAIProviderType.Custom}. */
26
+ export declare function getPreset(type: PhotonAIProviderType): ProviderPreset | null;
27
+ /** Re-exported so a consumer's custom `responseTransformer` can reuse Photon AI's validated JSON→response coercion. */
28
+ export { coerceGeneration, parseGenerationText };
29
+ //# sourceMappingURL=presets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../../src/photon-ai/provider/presets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAuB,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEhG;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,2EAA2E;IAC3E,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,0CAA0C;IAC1C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;IAC5E,oFAAoF;IACpF,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjE,iEAAiE;IACjE,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC;IACpD,wDAAwD;IACxD,iBAAiB,CAAC,QAAQ,EAAE,OAAO,GAAG,gBAAgB,CAAC;CACxD;AA8GD,yFAAyF;AACzF,wBAAgB,SAAS,CAAC,IAAI,EAAE,oBAAoB,GAAG,cAAc,GAAG,IAAI,CAE3E;AAED,uHAAuH;AACvH,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC"}
@@ -0,0 +1,110 @@
1
+ import { PhotonAIProviderType } from '../../types/photon-ai.types';
2
+ import { PhotonAIErrorKind, PhotonAIProviderError, } from './ai-provider.types';
3
+ import { buildUserPromptText, coerceGeneration, parseGenerationText } from './response-parsing';
4
+ /** Substitutes `{model}` and appends `key=` to a URL, respecting any existing query string. */
5
+ function geminiUrl(apiUrl, model, apiKey) {
6
+ const withModel = apiUrl.replace('{model}', encodeURIComponent(model));
7
+ if (!apiKey)
8
+ return withModel;
9
+ const separator = withModel.includes('?') ? '&' : '?';
10
+ return `${withModel}${separator}key=${encodeURIComponent(apiKey)}`;
11
+ }
12
+ /** Reads a nested text field out of an arbitrary parsed JSON body via a path of keys/indices. Returns '' if any hop is missing. */
13
+ function readText(source, path) {
14
+ let current = source;
15
+ for (const key of path) {
16
+ if (current == null)
17
+ return '';
18
+ current = current[key];
19
+ }
20
+ return typeof current === 'string' ? current : '';
21
+ }
22
+ const GEMINI_PRESET = {
23
+ defaultApiUrl: 'https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent',
24
+ defaultModel: 'gemini-2.5-flash',
25
+ buildUrl: geminiUrl,
26
+ buildHeaders: () => ({}),
27
+ transformRequest: (request) => ({
28
+ systemInstruction: { parts: [{ text: request.systemInstruction }] },
29
+ contents: [{ role: 'user', parts: [{ text: buildUserPromptText(request) }] }],
30
+ generationConfig: {
31
+ // Forces Gemini to always return a parseable JSON object.
32
+ responseMimeType: 'application/json',
33
+ temperature: request.temperature,
34
+ },
35
+ }),
36
+ transformResponse: (response) => {
37
+ const payload = response;
38
+ if (payload.promptFeedback?.blockReason) {
39
+ throw new PhotonAIProviderError(PhotonAIErrorKind.Blocked, `Gemini blocked the request (${payload.promptFeedback.blockReason}).`);
40
+ }
41
+ const candidate = payload.candidates?.[0];
42
+ if (!candidate) {
43
+ throw new PhotonAIProviderError(PhotonAIErrorKind.Blocked, 'Gemini returned no answer for that request.');
44
+ }
45
+ const text = candidate.content?.parts?.map((p) => p.text ?? '').join('') ?? '';
46
+ return parseGenerationText(text);
47
+ },
48
+ };
49
+ const OPENAI_PRESET = {
50
+ defaultApiUrl: 'https://api.openai.com/v1/chat/completions',
51
+ defaultModel: 'gpt-4o-mini',
52
+ buildUrl: (apiUrl) => apiUrl,
53
+ buildHeaders: (apiKey) => {
54
+ const headers = {};
55
+ if (apiKey)
56
+ headers.Authorization = `Bearer ${apiKey}`;
57
+ return headers;
58
+ },
59
+ transformRequest: (request) => ({
60
+ model: request.model,
61
+ temperature: request.temperature,
62
+ // Forces a JSON object back (system instruction already mentions JSON, as OpenAI requires).
63
+ response_format: { type: 'json_object' },
64
+ messages: [
65
+ { role: 'system', content: request.systemInstruction },
66
+ { role: 'user', content: buildUserPromptText(request) },
67
+ ],
68
+ }),
69
+ transformResponse: (response) => parseGenerationText(readText(response, ['choices', 0, 'message', 'content'])),
70
+ };
71
+ const ANTHROPIC_PRESET = {
72
+ defaultApiUrl: 'https://api.anthropic.com/v1/messages',
73
+ defaultModel: 'claude-haiku-4-5-20251001',
74
+ buildUrl: (apiUrl) => apiUrl,
75
+ buildHeaders: (apiKey) => {
76
+ const headers = {
77
+ 'anthropic-version': '2023-06-01',
78
+ // Required for calling the Anthropic API directly from a browser.
79
+ 'anthropic-dangerous-direct-browser-access': 'true',
80
+ };
81
+ if (apiKey)
82
+ headers['x-api-key'] = apiKey;
83
+ return headers;
84
+ },
85
+ transformRequest: (request) => ({
86
+ model: request.model,
87
+ max_tokens: 1024,
88
+ temperature: request.temperature,
89
+ system: request.systemInstruction,
90
+ messages: [{ role: 'user', content: buildUserPromptText(request) }],
91
+ }),
92
+ transformResponse: (response) => parseGenerationText(readText(response, ['content', 0, 'text'])),
93
+ };
94
+ /**
95
+ * Built-in presets by provider type. {@link PhotonAIProviderType.Custom} has no
96
+ * preset — a custom setup must supply its endpoint and both transformers.
97
+ */
98
+ const PRESETS = {
99
+ [PhotonAIProviderType.Gemini]: GEMINI_PRESET,
100
+ [PhotonAIProviderType.OpenAI]: OPENAI_PRESET,
101
+ [PhotonAIProviderType.Anthropic]: ANTHROPIC_PRESET,
102
+ [PhotonAIProviderType.Custom]: null,
103
+ };
104
+ /** The preset for a provider type, or `null` for {@link PhotonAIProviderType.Custom}. */
105
+ export function getPreset(type) {
106
+ return PRESETS[type] ?? null;
107
+ }
108
+ /** Re-exported so a consumer's custom `responseTransformer` can reuse Photon AI's validated JSON→response coercion. */
109
+ export { coerceGeneration, parseGenerationText };
110
+ //# sourceMappingURL=presets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.js","sourceRoot":"","sources":["../../../src/photon-ai/provider/presets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { type PhotonAIAction, type PhotonAIRequest, type PhotonAIResponse } from './ai-provider.types';
2
+ /**
3
+ * Provider-agnostic helpers for turning a model's raw text/JSON into a
4
+ * validated {@link PhotonAIResponse}, and for composing the user turn. Shared
5
+ * by every built-in provider preset so the JSON-recovery and validation logic
6
+ * lives in exactly one place regardless of which API produced the text.
7
+ */
8
+ /** The prompt text for the user turn: serialized grid context followed by the command. Reused by every preset. */
9
+ export declare function buildUserPromptText(request: PhotonAIRequest): string;
10
+ /**
11
+ * Parses the model's JSON *text* into a {@link PhotonAIResponse}. Tolerant of
12
+ * stray characters around the JSON object (some models wrap it), and validates
13
+ * that the result carries at least an action or a reply.
14
+ */
15
+ export declare function parseGenerationText(text: string): PhotonAIResponse;
16
+ /** Validates and normalizes an already-parsed JSON value into a {@link PhotonAIResponse}. Used by response transformers that receive an object. */
17
+ export declare function coerceGeneration(parsed: unknown): PhotonAIResponse;
18
+ /** Coerces the raw `actions` field into a validated, well-typed array — dropping any malformed entries. */
19
+ export declare function normalizeActions(raw: unknown): readonly PhotonAIAction[];
20
+ /** Last-ditch recovery: extract the first balanced `{...}` object from noisy model text. Returns `undefined` when none parses. */
21
+ export declare function tryRecoverJsonObject(text: string): unknown;
22
+ //# sourceMappingURL=response-parsing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-parsing.d.ts","sourceRoot":"","sources":["../../../src/photon-ai/provider/response-parsing.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAC;AAE7B;;;;;GAKG;AAEH,kHAAkH;AAClH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAEpE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAoBlE;AAED,mJAAmJ;AACnJ,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,gBAAgB,CAiBlE;AAED,2GAA2G;AAC3G,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,cAAc,EAAE,CAcxE;AAED,kIAAkI;AAClI,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAS1D"}
@@ -0,0 +1,79 @@
1
+ import { serializeGridContext } from './system-prompt';
2
+ import { PhotonAIErrorKind, PhotonAIProviderError, } from './ai-provider.types';
3
+ /**
4
+ * Provider-agnostic helpers for turning a model's raw text/JSON into a
5
+ * validated {@link PhotonAIResponse}, and for composing the user turn. Shared
6
+ * by every built-in provider preset so the JSON-recovery and validation logic
7
+ * lives in exactly one place regardless of which API produced the text.
8
+ */
9
+ /** The prompt text for the user turn: serialized grid context followed by the command. Reused by every preset. */
10
+ export function buildUserPromptText(request) {
11
+ return `${serializeGridContext(request.gridContext)}\n\nUSER COMMAND:\n${request.userCommand}`;
12
+ }
13
+ /**
14
+ * Parses the model's JSON *text* into a {@link PhotonAIResponse}. Tolerant of
15
+ * stray characters around the JSON object (some models wrap it), and validates
16
+ * that the result carries at least an action or a reply.
17
+ */
18
+ export function parseGenerationText(text) {
19
+ const trimmed = text.trim();
20
+ if (!trimmed) {
21
+ throw new PhotonAIProviderError(PhotonAIErrorKind.InvalidResponse, 'The model returned an empty response.');
22
+ }
23
+ let parsed;
24
+ try {
25
+ parsed = JSON.parse(trimmed);
26
+ }
27
+ catch {
28
+ parsed = tryRecoverJsonObject(trimmed);
29
+ if (parsed === undefined) {
30
+ throw new PhotonAIProviderError(PhotonAIErrorKind.InvalidResponse, 'The model returned malformed JSON that could not be parsed.');
31
+ }
32
+ }
33
+ return coerceGeneration(parsed);
34
+ }
35
+ /** Validates and normalizes an already-parsed JSON value into a {@link PhotonAIResponse}. Used by response transformers that receive an object. */
36
+ export function coerceGeneration(parsed) {
37
+ if (typeof parsed !== 'object' || parsed === null) {
38
+ throw new PhotonAIProviderError(PhotonAIErrorKind.InvalidResponse, 'The model response was not a JSON object.');
39
+ }
40
+ const record = parsed;
41
+ const actions = normalizeActions(record.actions);
42
+ const reply = typeof record.reply === 'string' ? record.reply.trim() : '';
43
+ if (actions.length === 0 && !reply) {
44
+ throw new PhotonAIProviderError(PhotonAIErrorKind.InvalidResponse, 'The model response contained neither an action nor a reply.');
45
+ }
46
+ return { actions, reply };
47
+ }
48
+ /** Coerces the raw `actions` field into a validated, well-typed array — dropping any malformed entries. */
49
+ export function normalizeActions(raw) {
50
+ if (!Array.isArray(raw))
51
+ return [];
52
+ const actions = [];
53
+ for (const entry of raw) {
54
+ if (typeof entry !== 'object' || entry === null)
55
+ continue;
56
+ const record = entry;
57
+ if (typeof record.type !== 'string' || !record.type)
58
+ continue;
59
+ const params = typeof record.params === 'object' && record.params !== null
60
+ ? record.params
61
+ : {};
62
+ actions.push({ type: record.type, params });
63
+ }
64
+ return actions;
65
+ }
66
+ /** Last-ditch recovery: extract the first balanced `{...}` object from noisy model text. Returns `undefined` when none parses. */
67
+ export function tryRecoverJsonObject(text) {
68
+ const start = text.indexOf('{');
69
+ const end = text.lastIndexOf('}');
70
+ if (start < 0 || end <= start)
71
+ return undefined;
72
+ try {
73
+ return JSON.parse(text.slice(start, end + 1));
74
+ }
75
+ catch {
76
+ return undefined;
77
+ }
78
+ }
79
+ //# sourceMappingURL=response-parsing.js.map