@theia/ai-core 1.61.0-next.8 → 1.61.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 (70) hide show
  1. package/lib/browser/ai-core-frontend-module.d.ts.map +1 -1
  2. package/lib/browser/ai-core-frontend-module.js +7 -0
  3. package/lib/browser/ai-core-frontend-module.js.map +1 -1
  4. package/lib/browser/ai-variable-uri-label-provider.d.ts +17 -0
  5. package/lib/browser/ai-variable-uri-label-provider.d.ts.map +1 -0
  6. package/lib/browser/ai-variable-uri-label-provider.js +85 -0
  7. package/lib/browser/ai-variable-uri-label-provider.js.map +1 -0
  8. package/lib/browser/file-variable-contribution.d.ts +9 -3
  9. package/lib/browser/file-variable-contribution.d.ts.map +1 -1
  10. package/lib/browser/file-variable-contribution.js +26 -8
  11. package/lib/browser/file-variable-contribution.js.map +1 -1
  12. package/lib/browser/frontend-prompt-customization-service.d.ts +22 -1
  13. package/lib/browser/frontend-prompt-customization-service.d.ts.map +1 -1
  14. package/lib/browser/frontend-prompt-customization-service.js +63 -25
  15. package/lib/browser/frontend-prompt-customization-service.js.map +1 -1
  16. package/lib/browser/frontend-variable-service.d.ts +28 -4
  17. package/lib/browser/frontend-variable-service.d.ts.map +1 -1
  18. package/lib/browser/frontend-variable-service.js +84 -1
  19. package/lib/browser/frontend-variable-service.js.map +1 -1
  20. package/lib/browser/token-usage-frontend-service-impl.d.ts.map +1 -1
  21. package/lib/browser/token-usage-frontend-service-impl.js +20 -2
  22. package/lib/browser/token-usage-frontend-service-impl.js.map +1 -1
  23. package/lib/browser/token-usage-frontend-service.d.ts +4 -0
  24. package/lib/browser/token-usage-frontend-service.d.ts.map +1 -1
  25. package/lib/browser/token-usage-frontend-service.js.map +1 -1
  26. package/lib/common/ai-variable-resource.d.ts +18 -0
  27. package/lib/common/ai-variable-resource.d.ts.map +1 -0
  28. package/lib/common/ai-variable-resource.js +103 -0
  29. package/lib/common/ai-variable-resource.js.map +1 -0
  30. package/lib/common/configurable-in-memory-resources.d.ts +45 -0
  31. package/lib/common/configurable-in-memory-resources.d.ts.map +1 -0
  32. package/lib/common/configurable-in-memory-resources.js +147 -0
  33. package/lib/common/configurable-in-memory-resources.js.map +1 -0
  34. package/lib/common/index.d.ts +2 -0
  35. package/lib/common/index.d.ts.map +1 -1
  36. package/lib/common/index.js +2 -0
  37. package/lib/common/index.js.map +1 -1
  38. package/lib/common/prompt-service.d.ts +16 -4
  39. package/lib/common/prompt-service.d.ts.map +1 -1
  40. package/lib/common/prompt-service.js +1 -1
  41. package/lib/common/prompt-service.js.map +1 -1
  42. package/lib/common/prompt-variable-contribution.d.ts +2 -1
  43. package/lib/common/prompt-variable-contribution.d.ts.map +1 -1
  44. package/lib/common/prompt-variable-contribution.js +10 -1
  45. package/lib/common/prompt-variable-contribution.js.map +1 -1
  46. package/lib/common/token-usage-service.d.ts +8 -0
  47. package/lib/common/token-usage-service.d.ts.map +1 -1
  48. package/lib/common/variable-service.d.ts +17 -2
  49. package/lib/common/variable-service.d.ts.map +1 -1
  50. package/lib/common/variable-service.js +43 -32
  51. package/lib/common/variable-service.js.map +1 -1
  52. package/lib/node/token-usage-service-impl.d.ts.map +1 -1
  53. package/lib/node/token-usage-service-impl.js +14 -1
  54. package/lib/node/token-usage-service-impl.js.map +1 -1
  55. package/package.json +11 -10
  56. package/src/browser/ai-core-frontend-module.ts +12 -5
  57. package/src/browser/ai-variable-uri-label-provider.ts +66 -0
  58. package/src/browser/file-variable-contribution.ts +34 -14
  59. package/src/browser/frontend-prompt-customization-service.ts +72 -25
  60. package/src/browser/frontend-variable-service.ts +115 -5
  61. package/src/browser/token-usage-frontend-service-impl.ts +27 -2
  62. package/src/browser/token-usage-frontend-service.ts +4 -0
  63. package/src/common/ai-variable-resource.ts +86 -0
  64. package/src/common/configurable-in-memory-resources.ts +156 -0
  65. package/src/common/index.ts +2 -0
  66. package/src/common/prompt-service.ts +14 -4
  67. package/src/common/prompt-variable-contribution.ts +10 -2
  68. package/src/common/token-usage-service.ts +8 -0
  69. package/src/common/variable-service.ts +58 -44
  70. package/src/node/token-usage-service-impl.ts +19 -1
@@ -0,0 +1,156 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclispeSource GmbH and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { injectable } from '@theia/core/shared/inversify';
18
+ import { SyncReferenceCollection, Reference, ResourceResolver, Resource, Event, Emitter, URI } from '@theia/core';
19
+ import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
20
+
21
+ @injectable()
22
+ /** For creating highly configurable in-memory resources */
23
+ export class ConfigurableInMemoryResources implements ResourceResolver {
24
+
25
+ protected readonly resources = new SyncReferenceCollection<string, ConfigurableMutableResource>(uri => new ConfigurableMutableResource(new URI(uri)));
26
+
27
+ get onWillDispose(): Event<ConfigurableMutableResource> {
28
+ return this.resources.onWillDispose;
29
+ }
30
+
31
+ add(uri: URI, options: ResourceInitializationOptions): ConfigurableMutableReferenceResource {
32
+ const resourceUri = uri.toString();
33
+ if (this.resources.has(resourceUri)) {
34
+ throw new Error(`Cannot add already existing in-memory resource '${resourceUri}'`);
35
+ }
36
+ const resource = this.acquire(resourceUri);
37
+ resource.update(options);
38
+ return resource;
39
+ }
40
+
41
+ update(uri: URI, options: ResourceInitializationOptions): Resource {
42
+ const resourceUri = uri.toString();
43
+ const resource = this.resources.get(resourceUri);
44
+ if (!resource) {
45
+ throw new Error(`Cannot update non-existent in-memory resource '${resourceUri}'`);
46
+ }
47
+ resource.update(options);
48
+ return resource;
49
+ }
50
+
51
+ resolve(uri: URI): ConfigurableMutableReferenceResource {
52
+ const uriString = uri.toString();
53
+ if (!this.resources.has(uriString)) {
54
+ throw new Error(`In memory '${uriString}' resource does not exist.`);
55
+ }
56
+ return this.acquire(uriString);
57
+ }
58
+
59
+ protected acquire(uri: string): ConfigurableMutableReferenceResource {
60
+ const reference = this.resources.acquire(uri);
61
+ return new ConfigurableMutableReferenceResource(reference);
62
+ }
63
+ }
64
+
65
+ export type ResourceInitializationOptions = Pick<Resource, 'autosaveable' | 'initiallyDirty' | 'readOnly'>
66
+ & { contents?: string | Promise<string>, onSave?: Resource['saveContents'] };
67
+
68
+ export class ConfigurableMutableResource implements Resource {
69
+ protected readonly onDidChangeContentsEmitter = new Emitter<void>();
70
+ readonly onDidChangeContents = this.onDidChangeContentsEmitter.event;
71
+ protected fireDidChangeContents(): void {
72
+ this.onDidChangeContentsEmitter.fire();
73
+ }
74
+
75
+ protected readonly onDidChangeReadonlyEmitter = new Emitter<boolean | MarkdownString>();
76
+ readonly onDidChangeReadOnly = this.onDidChangeReadonlyEmitter.event;
77
+
78
+ constructor(readonly uri: URI, protected options?: ResourceInitializationOptions) { }
79
+
80
+ get readOnly(): Resource['readOnly'] {
81
+ return this.options?.readOnly;
82
+ }
83
+
84
+ get autosaveable(): boolean {
85
+ return this.options?.autosaveable !== false;
86
+ }
87
+
88
+ get initiallyDirty(): boolean {
89
+ return !!this.options?.initiallyDirty;
90
+ }
91
+
92
+ readContents(): Promise<string> {
93
+ return Promise.resolve(this.options?.contents ?? '');
94
+ }
95
+
96
+ async saveContents(contents: string): Promise<void> {
97
+ await this.options?.onSave?.(contents);
98
+ this.update({ contents });
99
+ }
100
+
101
+ update(options: ResourceInitializationOptions): void {
102
+ const didContentsChange = 'contents' in options && options.contents !== this.options?.contents;
103
+ const didReadOnlyChange = 'readOnly' in options && options.readOnly !== this.options?.readOnly;
104
+ this.options = { ...this.options, ...options };
105
+ if (didContentsChange) {
106
+ this.onDidChangeContentsEmitter.fire();
107
+ }
108
+ if (didReadOnlyChange) {
109
+ this.onDidChangeReadonlyEmitter.fire(this.readOnly ?? false);
110
+ }
111
+ }
112
+
113
+ dispose(): void {
114
+ this.onDidChangeContentsEmitter.dispose();
115
+ }
116
+ }
117
+
118
+ export class ConfigurableMutableReferenceResource implements Resource {
119
+ constructor(protected reference: Reference<ConfigurableMutableResource>) { }
120
+
121
+ get uri(): URI {
122
+ return this.reference.object.uri;
123
+ }
124
+
125
+ get onDidChangeContents(): Event<void> {
126
+ return this.reference.object.onDidChangeContents;
127
+ }
128
+
129
+ dispose(): void {
130
+ this.reference.dispose();
131
+ }
132
+
133
+ readContents(): Promise<string> {
134
+ return this.reference.object.readContents();
135
+ }
136
+
137
+ saveContents(contents: string): Promise<void> {
138
+ return this.reference.object.saveContents(contents);
139
+ }
140
+
141
+ update(options: ResourceInitializationOptions): void {
142
+ this.reference.object.update(options);
143
+ }
144
+
145
+ get readOnly(): Resource['readOnly'] {
146
+ return this.reference.object.readOnly;
147
+ }
148
+
149
+ get initiallyDirty(): boolean {
150
+ return this.reference.object.initiallyDirty;
151
+ }
152
+
153
+ get autosaveable(): boolean {
154
+ return this.reference.object.autosaveable;
155
+ }
156
+ }
@@ -30,3 +30,5 @@ export * from './variable-service';
30
30
  export * from './settings-service';
31
31
  export * from './language-model-service';
32
32
  export * from './token-usage-service';
33
+ export * from './ai-variable-resource';
34
+ export * from './configurable-in-memory-resources';
@@ -84,7 +84,7 @@ export interface PromptService {
84
84
  *
85
85
  * @param id the id of the prompt
86
86
  * @param args the object with placeholders, mapping the placeholder key to the value
87
- * @param context the {@link AIVariableContext} to use during variable resolvement
87
+ * @param context the {@link AIVariableContext} to use during variable resolution
88
88
  * @param resolveVariable the variable resolving method. Fall back to using the {@link AIVariableService} if not given.
89
89
  */
90
90
  getPromptFragment(
@@ -202,9 +202,19 @@ export interface PromptCustomizationService {
202
202
  readonly onDidChangeCustomAgents: Event<void>;
203
203
 
204
204
  /**
205
- * Open the custom agent yaml file.
205
+ * Returns all locations of existing customAgents.yml files and potential locations where
206
+ * new customAgents.yml files could be created.
207
+ *
208
+ * @returns An array of objects containing the URI and whether the file exists
209
+ */
210
+ getCustomAgentsLocations(): Promise<{ uri: URI, exists: boolean }[]>;
211
+
212
+ /**
213
+ * Opens an existing customAgents.yml file at the given URI, or creates a new one if it doesn't exist.
214
+ *
215
+ * @param uri The URI of the customAgents.yml file to open or create
206
216
  */
207
- openCustomAgentYaml(): void;
217
+ openCustomAgentYaml(uri: URI): Promise<void>;
208
218
  }
209
219
 
210
220
  @injectable()
@@ -331,7 +341,7 @@ export class PromptServiceImpl implements PromptService {
331
341
  *
332
342
  * @param template the unresolved template text
333
343
  * @param args the object with placeholders, mapping the placeholder key to the value
334
- * @param context the {@link AIVariableContext} to use during variable resolvement
344
+ * @param context the {@link AIVariableContext} to use during variable resolution
335
345
  * @param resolveVariable the variable resolving method. Fall back to using the {@link AIVariableService} if not given.
336
346
  */
337
347
  protected async getVariableAndArgReplacements(
@@ -13,7 +13,7 @@
13
13
  //
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
- import { CommandService, nls } from '@theia/core';
16
+ import { CommandService, ILogger, nls } from '@theia/core';
17
17
  import { injectable, inject, optional } from '@theia/core/shared/inversify';
18
18
  import * as monaco from '@theia/monaco-editor-core';
19
19
  import {
@@ -50,6 +50,9 @@ export class PromptVariableContribution implements AIVariableContribution, AIVar
50
50
  @inject(PromptCustomizationService) @optional()
51
51
  protected readonly promptCustomizationService: PromptCustomizationService;
52
52
 
53
+ @inject(ILogger)
54
+ protected logger: ILogger;
55
+
53
56
  registerVariables(service: AIVariableService): void {
54
57
  service.registerResolver(PROMPT_VARIABLE, this);
55
58
  service.registerArgumentPicker(PROMPT_VARIABLE, this.triggerArgumentPicker.bind(this));
@@ -81,7 +84,12 @@ export class PromptVariableContribution implements AIVariableContribution, AIVar
81
84
  }
82
85
  }
83
86
  }
84
- return undefined;
87
+ this.logger.debug(`Could not resolve prompt variable '${request.variable.name}' with arg '${request.arg}'. Returning empty string.`);
88
+ return {
89
+ variable: request.variable,
90
+ value: '',
91
+ allResolvedDependencies: []
92
+ };
85
93
  }
86
94
 
87
95
  protected async triggerArgumentPicker(): Promise<string | undefined> {
@@ -23,6 +23,10 @@ export interface TokenUsage {
23
23
  inputTokens: number;
24
24
  /** The output token count */
25
25
  outputTokens: number;
26
+ /** Input tokens written to cache */
27
+ cachedInputTokens?: number;
28
+ /** Input tokens read from cache */
29
+ readCachedInputTokens?: number;
26
30
  /** The model identifier */
27
31
  model: string;
28
32
  /** The timestamp of when the tokens were used */
@@ -36,6 +40,10 @@ export interface TokenUsageParams {
36
40
  inputTokens: number;
37
41
  /** The output token count */
38
42
  outputTokens: number;
43
+ /** Input tokens placed in cache */
44
+ cachedInputTokens?: number;
45
+ /** Input tokens read from cache */
46
+ readCachedInputTokens?: number;
39
47
  /** Request identifier */
40
48
  requestId: string;
41
49
  }
@@ -120,6 +120,13 @@ export namespace AIVariableResolutionRequest {
120
120
  'variable' in arg &&
121
121
  typeof (arg as { variable: { name: unknown } }).variable.name === 'string';
122
122
  }
123
+
124
+ export function fromResolved(arg: ResolvedAIContextVariable): AIVariableResolutionRequest {
125
+ return {
126
+ variable: arg.variable,
127
+ arg: arg.arg
128
+ };
129
+ }
123
130
  }
124
131
 
125
132
  export interface AIVariableContext {
@@ -136,6 +143,11 @@ export interface AIVariableResolver {
136
143
  resolve(request: AIVariableResolutionRequest, context: AIVariableContext): Promise<ResolvedAIVariable | undefined>;
137
144
  }
138
145
 
146
+ export interface AIVariableOpener {
147
+ canOpen(request: AIVariableResolutionRequest, context: AIVariableContext): MaybePromise<number>;
148
+ open(request: AIVariableResolutionRequest, context: AIVariableContext): Promise<void>;
149
+ }
150
+
139
151
  export interface AIVariableResolverWithVariableDependencies extends AIVariableResolver {
140
152
  resolve(request: AIVariableResolutionRequest, context: AIVariableContext): Promise<ResolvedAIVariable | undefined>;
141
153
  /**
@@ -166,6 +178,7 @@ export interface AIVariableService {
166
178
  registerResolver(variable: AIVariable, resolver: AIVariableResolver): Disposable;
167
179
  unregisterResolver(variable: AIVariable, resolver: AIVariableResolver): void;
168
180
  getResolver(name: string, arg: string | undefined, context: AIVariableContext): Promise<AIVariableResolver | undefined>;
181
+ resolveVariable(variable: AIVariableArg, context: AIVariableContext, cache?: Map<string, ResolveAIVariableCacheEntry>): Promise<ResolvedAIVariable | undefined>;
169
182
 
170
183
  registerArgumentPicker(variable: AIVariable, argPicker: AIVariableArgPicker): Disposable;
171
184
  unregisterArgumentPicker(variable: AIVariable, argPicker: AIVariableArgPicker): void;
@@ -174,8 +187,6 @@ export interface AIVariableService {
174
187
  registerArgumentCompletionProvider(variable: AIVariable, argPicker: AIVariableArgCompletionProvider): Disposable;
175
188
  unregisterArgumentCompletionProvider(variable: AIVariable, argPicker: AIVariableArgCompletionProvider): void;
176
189
  getArgumentCompletionProvider(name: string): Promise<AIVariableArgCompletionProvider | undefined>;
177
-
178
- resolveVariable(variable: AIVariableArg, context: AIVariableContext, cache?: Map<string, ResolveAIVariableCacheEntry>): Promise<ResolvedAIVariable | undefined>;
179
190
  }
180
191
 
181
192
  /** Contributions on the frontend can optionally implement `FrontendVariableContribution`. */
@@ -191,7 +202,7 @@ export interface ResolveAIVariableCacheEntry {
191
202
 
192
203
  export type ResolveAIVariableCache = Map<string, ResolveAIVariableCacheEntry>;
193
204
  /**
194
- * Creates a new, empty cache for AI variable resolvement to hand into `AIVariableService.resolveVariable`.
205
+ * Creates a new, empty cache for AI variable resolution to hand into `AIVariableService.resolveVariable`.
195
206
  */
196
207
  export function createAIResolveVariableCache(): Map<string, ResolveAIVariableCacheEntry> {
197
208
  return new Map();
@@ -216,6 +227,7 @@ export class DefaultAIVariableService implements AIVariableService {
216
227
  protected variables = new Map<string, AIVariable>();
217
228
  protected resolvers = new Map<string, AIVariableResolver[]>();
218
229
  protected argPickers = new Map<string, AIVariableArgPicker>();
230
+ protected openers = new Map<string, AIVariableOpener[]>();
219
231
  protected argCompletionProviders = new Map<string, AIVariableArgCompletionProvider>();
220
232
 
221
233
  protected readonly onDidChangeVariablesEmitter = new Emitter<void>();
@@ -225,8 +237,7 @@ export class DefaultAIVariableService implements AIVariableService {
225
237
  @inject(ContributionProvider) @named(AIVariableContribution)
226
238
  protected readonly contributionProvider: ContributionProvider<AIVariableContribution>,
227
239
  @inject(ILogger) protected readonly logger: ILogger
228
- ) {
229
- }
240
+ ) { }
230
241
 
231
242
  protected initContributions(): void {
232
243
  this.contributionProvider.getContributions().forEach(contribution => contribution.registerVariables(this));
@@ -339,22 +350,27 @@ export class DefaultAIVariableService implements AIVariableService {
339
350
  return this.argCompletionProviders.get(this.getKey(name)) ?? undefined;
340
351
  }
341
352
 
342
- async resolveVariable(
343
- request: AIVariableArg,
344
- context: AIVariableContext,
345
- cache: ResolveAIVariableCache = createAIResolveVariableCache()
346
- ): Promise<ResolvedAIVariable | undefined> {
347
- // Calculate unique variable cache key from variable name and argument
353
+ protected parseRequest(request: AIVariableArg): { variableName: string, arg: string | undefined } {
348
354
  const variableName = typeof request === 'string'
349
355
  ? request
350
356
  : typeof request.variable === 'string'
351
357
  ? request.variable
352
358
  : request.variable.name;
353
359
  const arg = typeof request === 'string' ? undefined : request.arg;
360
+ return { variableName, arg };
361
+ }
362
+
363
+ async resolveVariable(
364
+ request: AIVariableArg,
365
+ context: AIVariableContext,
366
+ cache: ResolveAIVariableCache = createAIResolveVariableCache()
367
+ ): Promise<ResolvedAIVariable | undefined> {
368
+ // Calculate unique variable cache key from variable name and argument
369
+ const { variableName, arg } = this.parseRequest(request);
354
370
  const cacheKey = `${variableName}${PromptText.VARIABLE_SEPARATOR_CHAR}${arg ?? ''}`;
355
371
 
356
372
  // If the current cache key exists and is still in progress, we reached a cycle.
357
- // If we reach it but it has been resolved, it was part of another resolvement branch and we can simply return it.
373
+ // If we reach it but it has been resolved, it was part of another resolution branch and we can simply return it.
358
374
  if (cache.has(cacheKey)) {
359
375
  const existingEntry = cache.get(cacheKey)!;
360
376
  if (existingEntry.inProgress) {
@@ -364,40 +380,38 @@ export class DefaultAIVariableService implements AIVariableService {
364
380
  return existingEntry.promise;
365
381
  }
366
382
 
367
- const entry: ResolveAIVariableCacheEntry = { promise: Promise.resolve(undefined), inProgress: true };
383
+ const entry: ResolveAIVariableCacheEntry = { promise: this.doResolve(variableName, arg, context, cache), inProgress: true };
384
+ entry.promise.finally(() => entry.inProgress = false);
368
385
  cache.set(cacheKey, entry);
369
386
 
370
- // Asynchronously resolves a variable, handling its dependencies while preventing cyclical resolution.
371
- // Selects the appropriate resolver and resolution strategy based on whether nested dependency resolution is supported.
372
- const promise = (async () => {
373
- const variable = this.getVariable(variableName);
374
- if (!variable) {
375
- return undefined;
376
- }
377
- const resolver = await this.getResolver(variableName, arg, context);
378
- let resolved: ResolvedAIVariable | undefined;
379
- if (isResolverWithDependencies(resolver)) {
380
- // Explicit cast needed because Typescript does not consider the method parameter length of the type guard at compile time
381
- resolved = await (resolver as AIVariableResolverWithVariableDependencies).resolve(
382
- { variable, arg },
383
- context,
384
- async (depRequest: AIVariableResolutionRequest) =>
385
- this.resolveVariable(depRequest, context, cache)
386
- );
387
- } else if (resolver) {
388
- // Explicit cast needed because Typescript does not consider the method parameter length of the type guard at compile time
389
- resolved = await (resolver as AIVariableResolver).resolve({ variable, arg }, context);
390
- } else {
391
- resolved = undefined;
392
- }
393
- return resolved ? { ...resolved, arg } : undefined;
394
- })();
395
-
396
- entry.promise = promise;
397
- promise.finally(() => {
398
- entry.inProgress = false;
399
- });
387
+ return entry.promise;
388
+ }
400
389
 
401
- return promise;
390
+ /**
391
+ * Asynchronously resolves a variable, handling its dependencies while preventing cyclical resolution.
392
+ * Selects the appropriate resolver and resolution strategy based on whether nested dependency resolution is supported.
393
+ */
394
+ protected async doResolve(variableName: string, arg: string | undefined, context: AIVariableContext, cache: ResolveAIVariableCache): Promise<ResolvedAIVariable | undefined> {
395
+ const variable = this.getVariable(variableName);
396
+ if (!variable) {
397
+ return undefined;
398
+ }
399
+ const resolver = await this.getResolver(variableName, arg, context);
400
+ let resolved: ResolvedAIVariable | undefined;
401
+ if (isResolverWithDependencies(resolver)) {
402
+ // Explicit cast needed because Typescript does not consider the method parameter length of the type guard at compile time
403
+ resolved = await (resolver as AIVariableResolverWithVariableDependencies).resolve(
404
+ { variable, arg },
405
+ context,
406
+ async (depRequest: AIVariableResolutionRequest) =>
407
+ this.resolveVariable(depRequest, context, cache)
408
+ );
409
+ } else if (resolver) {
410
+ // Explicit cast needed because Typescript does not consider the method parameter length of the type guard at compile time
411
+ resolved = await (resolver as AIVariableResolver).resolve({ variable, arg }, context);
412
+ } else {
413
+ resolved = undefined;
414
+ }
415
+ return resolved ? { ...resolved, arg } : undefined;
402
416
  }
403
417
  }
@@ -41,6 +41,8 @@ export class TokenUsageServiceImpl implements TokenUsageService {
41
41
  async recordTokenUsage(model: string, params: TokenUsageParams): Promise<void> {
42
42
  const usage: TokenUsage = {
43
43
  inputTokens: params.inputTokens,
44
+ cachedInputTokens: params.cachedInputTokens,
45
+ readCachedInputTokens: params.readCachedInputTokens,
44
46
  outputTokens: params.outputTokens,
45
47
  model,
46
48
  timestamp: new Date(),
@@ -50,7 +52,23 @@ export class TokenUsageServiceImpl implements TokenUsageService {
50
52
  this.tokenUsages.push(usage);
51
53
  this.client?.notifyTokenUsage(usage);
52
54
 
53
- console.log(`Input Tokens: ${params.inputTokens}; Output Tokens: ${params.outputTokens}; Model: ${model}${params.requestId ? `; RequestId: ${params.requestId}` : ''}`);
55
+ let logMessage = `Input Tokens: ${params.inputTokens};`;
56
+
57
+ if (params.cachedInputTokens) {
58
+ logMessage += ` Input Tokens written to cache: ${params.cachedInputTokens};`;
59
+ }
60
+
61
+ if (params.readCachedInputTokens) {
62
+ logMessage += ` Input Tokens read from cache: ${params.readCachedInputTokens};`;
63
+ }
64
+
65
+ logMessage += ` Output Tokens: ${params.outputTokens}; Model: ${model};`;
66
+
67
+ if (params.requestId) {
68
+ logMessage += `; RequestId: ${params.requestId}`;
69
+ }
70
+
71
+ console.debug(logMessage);
54
72
  // For now we just store in memory
55
73
  // In the future, this could be persisted to disk, a database, or sent to a service
56
74
  return Promise.resolve();