@theia/ai-copilot 1.76.0-next.7 → 1.76.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +2 -1
  2. package/lib/browser/copilot-auth-dialog.d.ts.map +1 -1
  3. package/lib/browser/copilot-auth-dialog.js +7 -7
  4. package/lib/browser/copilot-auth-dialog.js.map +1 -1
  5. package/lib/browser/copilot-frontend-application-contribution.d.ts +6 -0
  6. package/lib/browser/copilot-frontend-application-contribution.d.ts.map +1 -1
  7. package/lib/browser/copilot-frontend-application-contribution.js +77 -2
  8. package/lib/browser/copilot-frontend-application-contribution.js.map +1 -1
  9. package/lib/common/copilot-language-models-manager.d.ts +5 -3
  10. package/lib/common/copilot-language-models-manager.d.ts.map +1 -1
  11. package/lib/common/copilot-language-models-manager.js.map +1 -1
  12. package/lib/node/copilot-language-models-manager-impl.d.ts +24 -2
  13. package/lib/node/copilot-language-models-manager-impl.d.ts.map +1 -1
  14. package/lib/node/copilot-language-models-manager-impl.js +79 -4
  15. package/lib/node/copilot-language-models-manager-impl.js.map +1 -1
  16. package/lib/node/copilot-language-models-manager-impl.spec.js +47 -4
  17. package/lib/node/copilot-language-models-manager-impl.spec.js.map +1 -1
  18. package/lib/node/copilot-sdk-language-model.d.ts +2 -2
  19. package/lib/node/copilot-sdk-language-model.d.ts.map +1 -1
  20. package/lib/node/copilot-sdk-language-model.js +4 -4
  21. package/lib/node/copilot-sdk-language-model.js.map +1 -1
  22. package/lib/node/copilot-sdk-language-model.spec.js +14 -1
  23. package/lib/node/copilot-sdk-language-model.spec.js.map +1 -1
  24. package/lib/node/copilot-sdk-mappers.d.ts +11 -6
  25. package/lib/node/copilot-sdk-mappers.d.ts.map +1 -1
  26. package/lib/node/copilot-sdk-mappers.js +28 -17
  27. package/lib/node/copilot-sdk-mappers.js.map +1 -1
  28. package/lib/node/copilot-sdk-mappers.spec.js +37 -2
  29. package/lib/node/copilot-sdk-mappers.spec.js.map +1 -1
  30. package/lib/node/copilot-sdk-types.d.ts +13 -3
  31. package/lib/node/copilot-sdk-types.d.ts.map +1 -1
  32. package/package.json +5 -5
  33. package/src/browser/copilot-auth-dialog.tsx +8 -8
  34. package/src/browser/copilot-frontend-application-contribution.ts +82 -2
  35. package/src/common/copilot-language-models-manager.ts +5 -3
  36. package/src/node/copilot-language-models-manager-impl.spec.ts +55 -4
  37. package/src/node/copilot-language-models-manager-impl.ts +86 -5
  38. package/src/node/copilot-sdk-language-model.spec.ts +17 -3
  39. package/src/node/copilot-sdk-language-model.ts +5 -4
  40. package/src/node/copilot-sdk-mappers.spec.ts +41 -2
  41. package/src/node/copilot-sdk-mappers.ts +31 -20
  42. package/src/node/copilot-sdk-types.ts +16 -2
@@ -14,14 +14,8 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { LanguageModelMessage } from '@theia/ai-core';
18
- import type { ModelInfo, SystemMessageConfig } from './copilot-sdk-types';
19
-
20
- /**
21
- * Recognizes an id that names a dated release of another model, such as
22
- * `gpt-4o-2024-11-20` or `claude-sonnet-5-20260514` for `gpt-4o` and `claude-sonnet-5`.
23
- */
24
- const DATED_MODEL_ID = /^(.+?)-(\d{4}-\d{2}-\d{2}|\d{8})$/;
17
+ import { Base64ImageContent, ImageContent, ImageMessage, LanguageModelMessage } from '@theia/ai-core';
18
+ import type { BlobMessageAttachment, ModelInfo, SystemMessageConfig } from './copilot-sdk-types';
25
19
 
26
20
  /**
27
21
  * Selects the model IDs that should be surfaced to Theia from the list returned
@@ -30,12 +24,12 @@ const DATED_MODEL_ID = /^(.+?)-(\d{4}-\d{2}-\d{2}|\d{8})$/;
30
24
  * Models whose policy is explicitly `disabled` are filtered out; `enabled` and
31
25
  * `unconfigured` models are kept. Order is preserved and duplicates are removed.
32
26
  *
33
- * The list can name the same model both by its family and by its dated releases. Offering the user
34
- * several entries that select the same model is noise, so a dated release is dropped when the family
35
- * it belongs to is offered as well. It is kept when it is the only way to select that model.
27
+ * The list can name the same model both by its family and by its dated releases, and both are kept:
28
+ * the family is what the model pickers offer, while a dated release stays selectable for anyone who
29
+ * wants to pin one. This mirrors the other providers, where discovery reports every release and only
30
+ * the undated ids are featured.
36
31
  */
37
32
  export function selectSdkModelIds(models: ModelInfo[]): string[] {
38
- const available = new Set(models.filter(model => model.id && model.policy?.state !== 'disabled').map(model => model.id));
39
33
  const result: string[] = [];
40
34
  const seen = new Set<string>();
41
35
  for (const model of models) {
@@ -45,10 +39,6 @@ export function selectSdkModelIds(models: ModelInfo[]): string[] {
45
39
  if (model.policy && model.policy.state === 'disabled') {
46
40
  continue;
47
41
  }
48
- const family = model.id.match(DATED_MODEL_ID)?.[1];
49
- if (family && available.has(family)) {
50
- continue;
51
- }
52
42
  seen.add(model.id);
53
43
  result.push(model.id);
54
44
  }
@@ -64,6 +54,8 @@ export interface SdkPrompt {
64
54
  systemText: string;
65
55
  /** The user-facing prompt body derived from the non-system messages. */
66
56
  prompt: string;
57
+ /** Base64-encoded images pulled out of the user's messages, sent alongside the prompt. */
58
+ attachments?: BlobMessageAttachment[];
67
59
  }
68
60
 
69
61
  function roleLabel(message: LanguageModelMessage): string {
@@ -77,6 +69,17 @@ function roleLabel(message: LanguageModelMessage): string {
77
69
  }
78
70
  }
79
71
 
72
+ /**
73
+ * Whether a message is a user image the SDK can be sent as a blob attachment.
74
+ *
75
+ * URL images are not covered: the SDK's attachments carry inline data, not a remote reference,
76
+ * and fetching the URL on the backend to inline it would add a server-side-request-forgery
77
+ * surface for what is, today, not a use case Theia exercises.
78
+ */
79
+ function isAttachableImageMessage(message: LanguageModelMessage): message is ImageMessage & { image: Base64ImageContent } {
80
+ return LanguageModelMessage.isImageMessage(message) && message.actor === 'user' && ImageContent.isBase64(message.image);
81
+ }
82
+
80
83
  function messageToText(message: LanguageModelMessage): string {
81
84
  if (LanguageModelMessage.isTextMessage(message)) {
82
85
  return message.text;
@@ -91,6 +94,7 @@ function messageToText(message: LanguageModelMessage): string {
91
94
  return `[tool result: ${content}]`;
92
95
  }
93
96
  if (LanguageModelMessage.isImageMessage(message)) {
97
+ // Reached for URL images and non-user image messages; base64 user images are attachments instead, see buildSdkPrompt.
94
98
  return '[image omitted]';
95
99
  }
96
100
  return '';
@@ -105,16 +109,23 @@ function messageToText(message: LanguageModelMessage): string {
105
109
  * see {@link buildSdkSystemMessage}. A lone user turn is forwarded verbatim;
106
110
  * richer histories are rendered as a role-labelled transcript.
107
111
  *
108
- * This is a lossy mapping by design, see the limitations documented in the package
109
- * README, and is intended for single-turn requests.
112
+ * Base64 user images are pulled out of the history and sent as blob attachments instead of
113
+ * being flattened into the prompt text, see {@link isAttachableImageMessage}. This is otherwise
114
+ * a lossy mapping by design, see the limitations documented in the package README, and is
115
+ * intended for single-turn requests.
110
116
  */
111
117
  export function buildSdkPrompt(messages: LanguageModelMessage[]): SdkPrompt {
112
118
  const systemParts: string[] = [];
113
119
  const conversation: LanguageModelMessage[] = [];
120
+ const attachments: BlobMessageAttachment[] = [];
114
121
  for (const message of messages) {
115
122
  if (message.actor === 'system' && LanguageModelMessage.isTextMessage(message)) {
116
123
  systemParts.push(message.text);
117
- } else if (message.type !== 'thinking') {
124
+ } else if (message.type === 'thinking') {
125
+ continue;
126
+ } else if (isAttachableImageMessage(message)) {
127
+ attachments.push({ type: 'blob', data: message.image.base64data, mimeType: message.image.mimeType });
128
+ } else {
118
129
  conversation.push(message);
119
130
  }
120
131
  }
@@ -131,7 +142,7 @@ export function buildSdkPrompt(messages: LanguageModelMessage[]): SdkPrompt {
131
142
  .trim();
132
143
  }
133
144
 
134
- return { systemText, prompt };
145
+ return { systemText, prompt, attachments: attachments.length > 0 ? attachments : undefined };
135
146
  }
136
147
 
137
148
  /**
@@ -191,9 +191,23 @@ export interface CopilotSessionEvents {
191
191
  export type CopilotSessionEventType = keyof CopilotSessionEvents;
192
192
 
193
193
  // ============================================================================
194
- // Session, from `dist/session.d.ts`
194
+ // Session, from `dist/session.d.ts` and `dist/types.d.ts`
195
195
  // ============================================================================
196
196
 
197
+ /** The one attachment kind this integration produces: an inline image, from `MessageOptions['attachments']`. */
198
+ export interface BlobMessageAttachment {
199
+ type: 'blob';
200
+ data: string;
201
+ mimeType: string;
202
+ displayName?: string;
203
+ }
204
+
205
+ /** The options this integration passes to {@link CopilotSession.send}. */
206
+ export interface MessageOptions {
207
+ prompt: string;
208
+ attachments?: BlobMessageAttachment[];
209
+ }
210
+
197
211
  /** A conversation with the runtime, upstream a class with resume, fork and sharing on top. */
198
212
  export interface CopilotSession {
199
213
  readonly sessionId: string;
@@ -202,7 +216,7 @@ export interface CopilotSession {
202
216
  */
203
217
  on<K extends CopilotSessionEventType>(eventType: K, handler: (event: CopilotSessionEvents[K]) => void): () => void;
204
218
  /** Sends a turn and resolves with the id of the message it created. */
205
- send(options: { prompt: string }): Promise<string>;
219
+ send(options: MessageOptions): Promise<string>;
206
220
  /** Stops the turn in flight. */
207
221
  abort(): Promise<void>;
208
222
  /** Releases the session in memory, leaving what it persisted in place. */