@elyracode/web-ui 0.9.10 → 0.9.12

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 (136) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/ChatPanel.d.ts +1 -1
  3. package/dist/ChatPanel.d.ts.map +1 -1
  4. package/dist/app.css +1 -1
  5. package/dist/components/AgentInterface.d.ts +1 -1
  6. package/dist/components/AgentInterface.d.ts.map +1 -1
  7. package/dist/components/AgentInterface.js +1 -1
  8. package/dist/components/AgentInterface.js.map +1 -1
  9. package/dist/components/MessageEditor.d.ts +2 -2
  10. package/dist/components/MessageEditor.d.ts.map +1 -1
  11. package/dist/components/Messages.d.ts +3 -3
  12. package/dist/components/Messages.d.ts.map +1 -1
  13. package/dist/components/Messages.js.map +1 -1
  14. package/dist/components/ProviderKeyInput.js +2 -2
  15. package/dist/components/ProviderKeyInput.js.map +1 -1
  16. package/dist/components/SandboxedIframe.d.ts +1 -1
  17. package/dist/components/SandboxedIframe.d.ts.map +1 -1
  18. package/dist/components/SandboxedIframe.js +1 -1
  19. package/dist/components/SandboxedIframe.js.map +1 -1
  20. package/dist/components/message-renderer-registry.js.map +1 -1
  21. package/dist/components/sandbox/ArtifactsRuntimeProvider.d.ts +4 -3
  22. package/dist/components/sandbox/ArtifactsRuntimeProvider.d.ts.map +1 -1
  23. package/dist/components/sandbox/ArtifactsRuntimeProvider.js +31 -31
  24. package/dist/components/sandbox/ArtifactsRuntimeProvider.js.map +1 -1
  25. package/dist/components/sandbox/AttachmentsRuntimeProvider.d.ts +1 -1
  26. package/dist/components/sandbox/AttachmentsRuntimeProvider.d.ts.map +1 -1
  27. package/dist/components/sandbox/AttachmentsRuntimeProvider.js +6 -5
  28. package/dist/components/sandbox/AttachmentsRuntimeProvider.js.map +1 -1
  29. package/dist/components/sandbox/ConsoleRuntimeProvider.d.ts +3 -2
  30. package/dist/components/sandbox/ConsoleRuntimeProvider.d.ts.map +1 -1
  31. package/dist/components/sandbox/ConsoleRuntimeProvider.js +12 -11
  32. package/dist/components/sandbox/ConsoleRuntimeProvider.js.map +1 -1
  33. package/dist/components/sandbox/FileDownloadRuntimeProvider.d.ts +3 -2
  34. package/dist/components/sandbox/FileDownloadRuntimeProvider.d.ts.map +1 -1
  35. package/dist/components/sandbox/FileDownloadRuntimeProvider.js +6 -4
  36. package/dist/components/sandbox/FileDownloadRuntimeProvider.js.map +1 -1
  37. package/dist/components/sandbox/RuntimeMessageRouter.d.ts +2 -1
  38. package/dist/components/sandbox/RuntimeMessageRouter.d.ts.map +1 -1
  39. package/dist/components/sandbox/RuntimeMessageRouter.js.map +1 -1
  40. package/dist/components/sandbox/SandboxRuntimeProvider.d.ts +3 -2
  41. package/dist/components/sandbox/SandboxRuntimeProvider.d.ts.map +1 -1
  42. package/dist/components/sandbox/runtime-messages.d.ts +140 -0
  43. package/dist/components/sandbox/runtime-messages.d.ts.map +1 -0
  44. package/dist/components/sandbox/runtime-messages.js +9 -0
  45. package/dist/components/sandbox/runtime-messages.js.map +1 -0
  46. package/dist/dialogs/AttachmentOverlay.d.ts.map +1 -1
  47. package/dist/dialogs/AttachmentOverlay.js +4 -4
  48. package/dist/dialogs/AttachmentOverlay.js.map +1 -1
  49. package/dist/dialogs/ModelSelector.d.ts +3 -3
  50. package/dist/dialogs/ModelSelector.d.ts.map +1 -1
  51. package/dist/dialogs/ModelSelector.js.map +1 -1
  52. package/dist/storage/stores/custom-providers-store.d.ts +2 -2
  53. package/dist/storage/stores/custom-providers-store.d.ts.map +1 -1
  54. package/dist/storage/types.d.ts +2 -2
  55. package/dist/storage/types.d.ts.map +1 -1
  56. package/dist/tools/artifacts/DocxArtifact.js +1 -1
  57. package/dist/tools/artifacts/DocxArtifact.js.map +1 -1
  58. package/dist/tools/artifacts/ExcelArtifact.js +1 -1
  59. package/dist/tools/artifacts/ExcelArtifact.js.map +1 -1
  60. package/dist/tools/artifacts/HtmlArtifact.js.map +1 -1
  61. package/dist/tools/artifacts/PdfArtifact.d.ts.map +1 -1
  62. package/dist/tools/artifacts/PdfArtifact.js +1 -1
  63. package/dist/tools/artifacts/PdfArtifact.js.map +1 -1
  64. package/dist/tools/artifacts/artifacts-tool-renderer.d.ts.map +1 -1
  65. package/dist/tools/artifacts/artifacts-tool-renderer.js.map +1 -1
  66. package/dist/tools/artifacts/artifacts.js.map +1 -1
  67. package/dist/tools/extract-document.js +1 -1
  68. package/dist/tools/extract-document.js.map +1 -1
  69. package/dist/tools/index.d.ts +1 -1
  70. package/dist/tools/index.d.ts.map +1 -1
  71. package/dist/tools/index.js.map +1 -1
  72. package/dist/tools/javascript-repl.js.map +1 -1
  73. package/dist/tools/renderer-registry.d.ts +4 -3
  74. package/dist/tools/renderer-registry.d.ts.map +1 -1
  75. package/dist/tools/renderer-registry.js.map +1 -1
  76. package/dist/tools/renderers/BashRenderer.d.ts.map +1 -1
  77. package/dist/tools/renderers/BashRenderer.js.map +1 -1
  78. package/dist/tools/renderers/CalculateRenderer.d.ts.map +1 -1
  79. package/dist/tools/renderers/CalculateRenderer.js.map +1 -1
  80. package/dist/tools/renderers/DefaultRenderer.d.ts +1 -1
  81. package/dist/tools/renderers/DefaultRenderer.d.ts.map +1 -1
  82. package/dist/tools/renderers/DefaultRenderer.js.map +1 -1
  83. package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts.map +1 -1
  84. package/dist/tools/renderers/GetCurrentTimeRenderer.js.map +1 -1
  85. package/dist/tools/types.d.ts +1 -1
  86. package/dist/tools/types.d.ts.map +1 -1
  87. package/dist/utils/attachment-utils.js +1 -1
  88. package/dist/utils/attachment-utils.js.map +1 -1
  89. package/dist/utils/format.d.ts +2 -2
  90. package/dist/utils/format.d.ts.map +1 -1
  91. package/dist/utils/format.js.map +1 -1
  92. package/dist/utils/model-discovery.d.ts +6 -6
  93. package/dist/utils/model-discovery.d.ts.map +1 -1
  94. package/dist/utils/model-discovery.js +3 -3
  95. package/dist/utils/model-discovery.js.map +1 -1
  96. package/dist/utils/proxy-utils.d.ts +1 -1
  97. package/example/package.json +1 -1
  98. package/package.json +5 -5
  99. package/src/ChatPanel.ts +1 -1
  100. package/src/components/AgentInterface.ts +5 -5
  101. package/src/components/MessageEditor.ts +2 -2
  102. package/src/components/Messages.ts +6 -6
  103. package/src/components/ProviderKeyInput.ts +3 -3
  104. package/src/components/SandboxedIframe.ts +7 -6
  105. package/src/components/message-renderer-registry.ts +1 -1
  106. package/src/components/sandbox/ArtifactsRuntimeProvider.ts +40 -44
  107. package/src/components/sandbox/AttachmentsRuntimeProvider.ts +9 -7
  108. package/src/components/sandbox/ConsoleRuntimeProvider.ts +19 -17
  109. package/src/components/sandbox/FileDownloadRuntimeProvider.ts +11 -7
  110. package/src/components/sandbox/RuntimeMessageRouter.ts +7 -8
  111. package/src/components/sandbox/SandboxRuntimeProvider.ts +4 -2
  112. package/src/components/sandbox/runtime-messages.ts +146 -0
  113. package/src/dialogs/AttachmentOverlay.ts +12 -11
  114. package/src/dialogs/CustomProviderDialog.ts +2 -2
  115. package/src/dialogs/ModelSelector.ts +11 -11
  116. package/src/storage/stores/custom-providers-store.ts +2 -2
  117. package/src/storage/types.ts +2 -2
  118. package/src/tools/artifacts/DocxArtifact.ts +2 -2
  119. package/src/tools/artifacts/ExcelArtifact.ts +3 -3
  120. package/src/tools/artifacts/HtmlArtifact.ts +1 -1
  121. package/src/tools/artifacts/PdfArtifact.ts +5 -4
  122. package/src/tools/artifacts/artifacts-tool-renderer.ts +4 -4
  123. package/src/tools/artifacts/artifacts.ts +4 -4
  124. package/src/tools/extract-document.ts +6 -6
  125. package/src/tools/index.ts +1 -1
  126. package/src/tools/javascript-repl.ts +3 -3
  127. package/src/tools/renderer-registry.ts +5 -5
  128. package/src/tools/renderers/BashRenderer.ts +3 -3
  129. package/src/tools/renderers/CalculateRenderer.ts +3 -3
  130. package/src/tools/renderers/DefaultRenderer.ts +5 -5
  131. package/src/tools/renderers/GetCurrentTimeRenderer.ts +5 -5
  132. package/src/tools/types.ts +1 -1
  133. package/src/utils/attachment-utils.ts +10 -3
  134. package/src/utils/format.ts +2 -2
  135. package/src/utils/model-discovery.ts +35 -22
  136. package/src/utils/proxy-utils.ts +1 -1
@@ -1,5 +1,5 @@
1
1
  import "@elyracode/mini-lit/dist/CodeBlock.js";
2
- import type { ToolResultMessage } from "@elyracode/ai";
2
+ import type { TextContent, ToolResultMessage } from "@elyracode/ai";
3
3
  import { createRef, ref } from "lit/directives/ref.js";
4
4
  import { FileCode2 } from "lucide";
5
5
  import "../../components/ConsoleBlock.js";
@@ -12,12 +12,12 @@ import { ArtifactPill } from "./ArtifactPill.js";
12
12
  import type { ArtifactsPanel, ArtifactsParams } from "./artifacts.js";
13
13
 
14
14
  // Helper to extract text from content blocks
15
- function getTextOutput(result: ToolResultMessage<any> | undefined): string {
15
+ function getTextOutput(result: ToolResultMessage<unknown> | undefined): string {
16
16
  if (!result) return "";
17
17
  return (
18
18
  result.content
19
- ?.filter((c) => c.type === "text")
20
- .map((c: any) => c.text)
19
+ ?.filter((c): c is TextContent => c.type === "text")
20
+ .map((c) => c.text)
21
21
  .join("\n") || ""
22
22
  );
23
23
  }
@@ -311,7 +311,7 @@ export class ArtifactsPanel extends LitElement {
311
311
  // 2) Build an ordered list of successful artifact operations
312
312
  const operations: Array<ArtifactsParams> = [];
313
313
  for (const m of messages) {
314
- if ((m as any).role === "artifact") {
314
+ if (m.role === "artifact") {
315
315
  const artifactMsg = m as ArtifactMessage;
316
316
  switch (artifactMsg.action) {
317
317
  case "create":
@@ -337,8 +337,8 @@ export class ArtifactsPanel extends LitElement {
337
337
  }
338
338
  }
339
339
  // Handle tool result messages (from artifacts tool calls)
340
- else if ((m as any).role === "toolResult" && (m as any).toolName === artifactToolName && !(m as any).isError) {
341
- const toolCallId = (m as any).toolCallId as string;
340
+ else if (m.role === "toolResult" && m.toolName === artifactToolName && !m.isError) {
341
+ const toolCallId = m.toolCallId;
342
342
  const call = toolCalls.get(toolCallId);
343
343
  if (!call) continue;
344
344
  const params = call.arguments as ArtifactsParams;
@@ -431,7 +431,7 @@ export class ArtifactsPanel extends LitElement {
431
431
  return this.getLogs(params);
432
432
  default:
433
433
  // Should never happen with TypeBox validation
434
- return `Error: Unknown command ${(params as any).command}`;
434
+ return `Error: Unknown command ${(params as { command: string }).command}`;
435
435
  }
436
436
  }
437
437
 
@@ -1,5 +1,5 @@
1
1
  import type { AgentTool } from "@elyracode/agent-core";
2
- import type { ToolResultMessage } from "@elyracode/ai";
2
+ import type { TextContent, ToolResultMessage } from "@elyracode/ai";
3
3
  import { html } from "lit";
4
4
  import { createRef, ref } from "lit/directives/ref.js";
5
5
  import { FileText } from "lucide";
@@ -103,17 +103,17 @@ export function createExtractDocumentTool(): AgentTool<typeof extractDocumentSch
103
103
  try {
104
104
  // Attempt direct fetch first
105
105
  arrayBuffer = await fetchAndProcess(url);
106
- } catch (directError: any) {
106
+ } catch (directError) {
107
107
  // If CORS error and proxy is available, retry with proxy
108
108
  if (isCorsError(directError) && tool.corsProxyUrl) {
109
109
  try {
110
110
  const proxiedUrl = tool.corsProxyUrl + encodeURIComponent(url);
111
111
  arrayBuffer = await fetchAndProcess(proxiedUrl);
112
- } catch (proxyError: any) {
112
+ } catch (proxyError) {
113
113
  // Proxy fetch also failed - throw helpful message
114
114
  throw new Error(
115
115
  `TELL USER: Unable to fetch the document due to CORS restrictions.\n\n` +
116
- `Tried with proxy but it also failed: ${proxyError.message}\n\n` +
116
+ `Tried with proxy but it also failed: ${proxyError instanceof Error ? proxyError.message : String(proxyError)}\n\n` +
117
117
  `INSTRUCT USER: Please download the file manually and attach it to your message using the attachment button (paperclip icon) in the message input area. I can then extract the text from the attached file.`,
118
118
  );
119
119
  }
@@ -213,8 +213,8 @@ export const extractDocumentRenderer: ToolRenderer<ExtractDocumentParams, Extrac
213
213
 
214
214
  const output =
215
215
  result.content
216
- ?.filter((c) => c.type === "text")
217
- .map((c: any) => c.text)
216
+ ?.filter((c): c is TextContent => c.type === "text")
217
+ .map((c) => c.text)
218
218
  .join("\n") || "";
219
219
 
220
220
  return {
@@ -27,7 +27,7 @@ export function setShowJsonMode(enabled: boolean): void {
27
27
  */
28
28
  export function renderTool(
29
29
  toolName: string,
30
- params: any | undefined,
30
+ params: unknown | undefined,
31
31
  result: ToolResultMessage | undefined,
32
32
  isStreaming?: boolean,
33
33
  ): ToolRenderResult {
@@ -1,5 +1,5 @@
1
1
  import type { AgentTool } from "@elyracode/agent-core";
2
- import type { ToolResultMessage } from "@elyracode/ai";
2
+ import type { TextContent, ToolResultMessage } from "@elyracode/ai";
3
3
  import { i18n } from "@elyracode/mini-lit";
4
4
  import { html } from "lit";
5
5
  import { createRef, ref } from "lit/directives/ref.js";
@@ -214,8 +214,8 @@ export const javascriptReplRenderer: ToolRenderer<JavaScriptReplParams, JavaScri
214
214
  if (result && params) {
215
215
  const output =
216
216
  result.content
217
- ?.filter((c) => c.type === "text")
218
- .map((c: any) => c.text)
217
+ ?.filter((c): c is TextContent => c.type === "text")
218
+ .map((c) => c.text)
219
219
  .join("\n") || "";
220
220
  const files = result.details?.files || [];
221
221
 
@@ -2,7 +2,7 @@ import { icon } from "@elyracode/mini-lit";
2
2
  import { html, type TemplateResult } from "lit";
3
3
  import type { Ref } from "lit/directives/ref.js";
4
4
  import { ref } from "lit/directives/ref.js";
5
- import { ChevronsUpDown, ChevronUp, Loader } from "lucide";
5
+ import { ChevronsUpDown, ChevronUp, type IconNode, Loader } from "lucide";
6
6
  import type { ToolRenderer } from "./types.js";
7
7
 
8
8
  // Registry of tool renderers
@@ -28,10 +28,10 @@ export function getToolRenderer(toolName: string): ToolRenderer | undefined {
28
28
  */
29
29
  export function renderHeader(
30
30
  state: "inprogress" | "complete" | "error",
31
- toolIcon: any,
31
+ toolIcon: IconNode,
32
32
  text: string | TemplateResult,
33
33
  ): TemplateResult {
34
- const statusIcon = (iconComponent: any, color: string) =>
34
+ const statusIcon = (iconComponent: IconNode, color: string) =>
35
35
  html`<span class="inline-block ${color}">${icon(iconComponent, "sm")}</span>`;
36
36
 
37
37
  switch (state) {
@@ -68,13 +68,13 @@ export function renderHeader(
68
68
  */
69
69
  export function renderCollapsibleHeader(
70
70
  state: "inprogress" | "complete" | "error",
71
- toolIcon: any,
71
+ toolIcon: IconNode,
72
72
  text: string | TemplateResult,
73
73
  contentRef: Ref<HTMLElement>,
74
74
  chevronRef: Ref<HTMLElement>,
75
75
  defaultExpanded = false,
76
76
  ): TemplateResult {
77
- const statusIcon = (iconComponent: any, color: string) =>
77
+ const statusIcon = (iconComponent: IconNode, color: string) =>
78
78
  html`<span class="inline-block ${color}">${icon(iconComponent, "sm")}</span>`;
79
79
 
80
80
  const toggleContent = (e: Event) => {
@@ -1,4 +1,4 @@
1
- import type { ToolResultMessage } from "@elyracode/ai";
1
+ import type { TextContent, ToolResultMessage } from "@elyracode/ai";
2
2
  import { html } from "lit";
3
3
  import { SquareTerminal } from "lucide";
4
4
  import { i18n } from "../../utils/i18n.js";
@@ -18,8 +18,8 @@ export class BashRenderer implements ToolRenderer<BashParams, undefined> {
18
18
  if (result && params?.command) {
19
19
  const output =
20
20
  result.content
21
- ?.filter((c) => c.type === "text")
22
- .map((c: any) => c.text)
21
+ ?.filter((c): c is TextContent => c.type === "text")
22
+ .map((c) => c.text)
23
23
  .join("\n") || "";
24
24
  const combined = output ? `> ${params.command}\n\n${output}` : `> ${params.command}`;
25
25
  return {
@@ -1,4 +1,4 @@
1
- import type { ToolResultMessage } from "@elyracode/ai";
1
+ import type { TextContent, ToolResultMessage } from "@elyracode/ai";
2
2
  import { html } from "lit";
3
3
  import { Calculator } from "lucide";
4
4
  import { i18n } from "../../utils/i18n.js";
@@ -18,8 +18,8 @@ export class CalculateRenderer implements ToolRenderer<CalculateParams, undefine
18
18
  if (result && params?.expression) {
19
19
  const output =
20
20
  result.content
21
- ?.filter((c) => c.type === "text")
22
- .map((c: any) => c.text)
21
+ ?.filter((c): c is TextContent => c.type === "text")
22
+ .map((c) => c.text)
23
23
  .join("\n") || "";
24
24
 
25
25
  // Error: show expression in header, error below
@@ -1,4 +1,4 @@
1
- import type { ToolResultMessage } from "@elyracode/ai";
1
+ import type { TextContent, ToolResultMessage } from "@elyracode/ai";
2
2
  import { html } from "lit";
3
3
  import { Code } from "lucide";
4
4
  import { i18n } from "../../utils/i18n.js";
@@ -6,14 +6,14 @@ import { renderHeader } from "../renderer-registry.js";
6
6
  import type { ToolRenderer, ToolRenderResult } from "../types.js";
7
7
 
8
8
  export class DefaultRenderer implements ToolRenderer {
9
- render(params: any | undefined, result: ToolResultMessage | undefined, isStreaming?: boolean): ToolRenderResult {
9
+ render(params: unknown | undefined, result: ToolResultMessage | undefined, isStreaming?: boolean): ToolRenderResult {
10
10
  const state = result ? (result.isError ? "error" : "complete") : isStreaming ? "inprogress" : "complete";
11
11
 
12
12
  // Format params as JSON
13
13
  let paramsJson = "";
14
14
  if (params) {
15
15
  try {
16
- paramsJson = JSON.stringify(JSON.parse(params), null, 2);
16
+ paramsJson = JSON.stringify(JSON.parse(params as string), null, 2);
17
17
  } catch {
18
18
  try {
19
19
  paramsJson = JSON.stringify(params, null, 2);
@@ -27,8 +27,8 @@ export class DefaultRenderer implements ToolRenderer {
27
27
  if (result) {
28
28
  let outputJson =
29
29
  result.content
30
- ?.filter((c) => c.type === "text")
31
- .map((c: any) => c.text)
30
+ ?.filter((c): c is TextContent => c.type === "text")
31
+ .map((c) => c.text)
32
32
  .join("\n") || i18n("(no output)");
33
33
  let outputLanguage = "text";
34
34
 
@@ -1,4 +1,4 @@
1
- import type { ToolResultMessage } from "@elyracode/ai";
1
+ import type { TextContent, ToolResultMessage } from "@elyracode/ai";
2
2
  import { html } from "lit";
3
3
  import { Clock } from "lucide";
4
4
  import { i18n } from "../../utils/i18n.js";
@@ -21,8 +21,8 @@ export class GetCurrentTimeRenderer implements ToolRenderer<GetCurrentTimeParams
21
21
  if (result && params) {
22
22
  const output =
23
23
  result.content
24
- ?.filter((c) => c.type === "text")
25
- .map((c: any) => c.text)
24
+ ?.filter((c): c is TextContent => c.type === "text")
25
+ .map((c) => c.text)
26
26
  .join("\n") || "";
27
27
  const headerText = params.timezone
28
28
  ? `${i18n("Getting current time in")} ${params.timezone}`
@@ -49,8 +49,8 @@ export class GetCurrentTimeRenderer implements ToolRenderer<GetCurrentTimeParams
49
49
  if (result) {
50
50
  const output =
51
51
  result.content
52
- ?.filter((c) => c.type === "text")
53
- .map((c: any) => c.text)
52
+ ?.filter((c): c is TextContent => c.type === "text")
53
+ .map((c) => c.text)
54
54
  .join("\n") || "";
55
55
 
56
56
  // Error: show header, error below
@@ -6,7 +6,7 @@ export interface ToolRenderResult {
6
6
  isCustom: boolean; // true = no card wrapper, false = wrap in card
7
7
  }
8
8
 
9
- export interface ToolRenderer<TParams = any, TDetails = any> {
9
+ export interface ToolRenderer<TParams = unknown, TDetails = unknown> {
10
10
  render(
11
11
  params: TParams | undefined,
12
12
  result: ToolResultMessage<TDetails> | undefined,
@@ -214,8 +214,8 @@ async function processPdf(
214
214
  const page = await pdf.getPage(i);
215
215
  const textContent = await page.getTextContent();
216
216
  const pageText = textContent.items
217
- .map((item: any) => item.str)
218
- .filter((str: string) => str.trim())
217
+ .map((item) => ("str" in item ? item.str : ""))
218
+ .filter((str) => str.trim())
219
219
  .join(" ");
220
220
  extractedText += `\n<page number="${i}">\n${pageText}\n</page>`;
221
221
  }
@@ -298,7 +298,14 @@ async function processDocx(arrayBuffer: ArrayBuffer, fileName: string): Promise<
298
298
  }
299
299
  }
300
300
 
301
- function extractTextFromElement(element: any): string {
301
+ /** Structural shape of a docx-preview document tree node (the library types its tree as `any`). */
302
+ interface DocxElement {
303
+ type?: string;
304
+ text?: string;
305
+ children?: DocxElement[];
306
+ }
307
+
308
+ function extractTextFromElement(element: DocxElement): string {
302
309
  let text = "";
303
310
 
304
311
  // Check type with lowercase
@@ -1,11 +1,11 @@
1
- import type { Usage } from "@elyracode/ai";
1
+ import type { Api, Model, Usage } from "@elyracode/ai";
2
2
  import { i18n } from "@elyracode/mini-lit";
3
3
 
4
4
  export function formatCost(cost: number): string {
5
5
  return `$${cost.toFixed(4)}`;
6
6
  }
7
7
 
8
- export function formatModelCost(cost: any): string {
8
+ export function formatModelCost(cost: Model<Api>["cost"] | undefined): string {
9
9
  if (!cost) return i18n("Free");
10
10
  const input = cost.input || 0;
11
11
  const output = cost.output || 0;
@@ -1,14 +1,27 @@
1
- import type { Model } from "@elyracode/ai";
1
+ import type { Api, Model } from "@elyracode/ai";
2
2
  import { LMStudioClient } from "@lmstudio/sdk";
3
3
  import { Ollama } from "ollama/browser";
4
4
 
5
+ /** Minimal shape of a model entry from an OpenAI-compatible /v1/models endpoint. */
6
+ interface OpenAICompatibleModel {
7
+ id: string;
8
+ context_length?: number;
9
+ max_tokens?: number;
10
+ max_model_len?: number;
11
+ }
12
+
13
+ /** Minimal shape of the response from an OpenAI-compatible /v1/models endpoint. */
14
+ interface OpenAICompatibleModelsResponse {
15
+ data?: OpenAICompatibleModel[];
16
+ }
17
+
5
18
  /**
6
19
  * Discover models from an Ollama server.
7
20
  * @param baseUrl - Base URL of the Ollama server (e.g., "http://localhost:11434")
8
21
  * @param apiKey - Optional API key (currently unused by Ollama)
9
22
  * @returns Array of discovered models
10
23
  */
11
- export async function discoverOllamaModels(baseUrl: string, _apiKey?: string): Promise<Model<any>[]> {
24
+ export async function discoverOllamaModels(baseUrl: string, _apiKey?: string): Promise<Model<Api>[]> {
12
25
  try {
13
26
  // Create Ollama client
14
27
  const ollama = new Ollama({ host: baseUrl });
@@ -17,7 +30,7 @@ export async function discoverOllamaModels(baseUrl: string, _apiKey?: string): P
17
30
  const { models } = await ollama.list();
18
31
 
19
32
  // Fetch details for each model and convert to Model format
20
- const ollamaModelPromises: Promise<Model<any> | null>[] = models.map(async (model: any) => {
33
+ const ollamaModelPromises: Promise<Model<Api> | null>[] = models.map(async (model) => {
21
34
  try {
22
35
  // Get model details
23
36
  const details = await ollama.show({
@@ -25,14 +38,14 @@ export async function discoverOllamaModels(baseUrl: string, _apiKey?: string): P
25
38
  });
26
39
 
27
40
  // Check capabilities - filter out models that don't support tools
28
- const capabilities: string[] = (details as any).capabilities || [];
41
+ const capabilities: string[] = details.capabilities || [];
29
42
  if (!capabilities.includes("tools")) {
30
43
  console.debug(`Skipping model ${model.name}: does not support tools`);
31
44
  return null;
32
45
  }
33
46
 
34
47
  // Extract model info
35
- const modelInfo: any = details.model_info || {};
48
+ const modelInfo = (details.model_info || {}) as unknown as Record<string, string>;
36
49
 
37
50
  // Get context window size - look for architecture-specific keys
38
51
  const architecture = modelInfo["general.architecture"] || "";
@@ -43,10 +56,10 @@ export async function discoverOllamaModels(baseUrl: string, _apiKey?: string): P
43
56
  const maxTokens = contextWindow * 10;
44
57
 
45
58
  // Ollama only supports completions API
46
- const ollamaModel: Model<any> = {
59
+ const ollamaModel: Model<Api> = {
47
60
  id: model.name,
48
61
  name: model.name,
49
- api: "openai-completions" as any,
62
+ api: "openai-completions",
50
63
  provider: "", // Will be set by caller
51
64
  baseUrl: `${baseUrl}/v1`,
52
65
  reasoning: capabilities.includes("thinking"),
@@ -69,7 +82,7 @@ export async function discoverOllamaModels(baseUrl: string, _apiKey?: string): P
69
82
  });
70
83
 
71
84
  const results = await Promise.all(ollamaModelPromises);
72
- return results.filter((m): m is Model<any> => m !== null);
85
+ return results.filter((m): m is Model<Api> => m !== null);
73
86
  } catch (err) {
74
87
  console.error("Failed to discover Ollama models:", err);
75
88
  throw new Error(`Ollama discovery failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -82,7 +95,7 @@ export async function discoverOllamaModels(baseUrl: string, _apiKey?: string): P
82
95
  * @param apiKey - Optional API key
83
96
  * @returns Array of discovered models
84
97
  */
85
- export async function discoverLlamaCppModels(baseUrl: string, apiKey?: string): Promise<Model<any>[]> {
98
+ export async function discoverLlamaCppModels(baseUrl: string, apiKey?: string): Promise<Model<Api>[]> {
86
99
  try {
87
100
  const headers: HeadersInit = {
88
101
  "Content-Type": "application/json",
@@ -101,21 +114,21 @@ export async function discoverLlamaCppModels(baseUrl: string, apiKey?: string):
101
114
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
102
115
  }
103
116
 
104
- const data = await response.json();
117
+ const data = (await response.json()) as OpenAICompatibleModelsResponse;
105
118
 
106
119
  if (!data.data || !Array.isArray(data.data)) {
107
120
  throw new Error("Invalid response format from llama.cpp server");
108
121
  }
109
122
 
110
- return data.data.map((model: any) => {
123
+ return data.data.map((model) => {
111
124
  // llama.cpp doesn't always provide context window info
112
125
  const contextWindow = model.context_length || 8192;
113
126
  const maxTokens = model.max_tokens || 4096;
114
127
 
115
- const llamaModel: Model<any> = {
128
+ const llamaModel: Model<Api> = {
116
129
  id: model.id,
117
130
  name: model.id,
118
- api: "openai-completions" as any,
131
+ api: "openai-completions",
119
132
  provider: "", // Will be set by caller
120
133
  baseUrl: `${baseUrl}/v1`,
121
134
  reasoning: false,
@@ -144,7 +157,7 @@ export async function discoverLlamaCppModels(baseUrl: string, apiKey?: string):
144
157
  * @param apiKey - Optional API key
145
158
  * @returns Array of discovered models
146
159
  */
147
- export async function discoverVLLMModels(baseUrl: string, apiKey?: string): Promise<Model<any>[]> {
160
+ export async function discoverVLLMModels(baseUrl: string, apiKey?: string): Promise<Model<Api>[]> {
148
161
  try {
149
162
  const headers: HeadersInit = {
150
163
  "Content-Type": "application/json",
@@ -163,21 +176,21 @@ export async function discoverVLLMModels(baseUrl: string, apiKey?: string): Prom
163
176
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
164
177
  }
165
178
 
166
- const data = await response.json();
179
+ const data = (await response.json()) as OpenAICompatibleModelsResponse;
167
180
 
168
181
  if (!data.data || !Array.isArray(data.data)) {
169
182
  throw new Error("Invalid response format from vLLM server");
170
183
  }
171
184
 
172
- return data.data.map((model: any) => {
185
+ return data.data.map((model) => {
173
186
  // vLLM provides max_model_len which is the context window
174
187
  const contextWindow = model.max_model_len || 8192;
175
188
  const maxTokens = Math.min(contextWindow, 4096); // Cap max tokens
176
189
 
177
- const vllmModel: Model<any> = {
190
+ const vllmModel: Model<Api> = {
178
191
  id: model.id,
179
192
  name: model.id,
180
- api: "openai-completions" as any,
193
+ api: "openai-completions",
181
194
  provider: "", // Will be set by caller
182
195
  baseUrl: `${baseUrl}/v1`,
183
196
  reasoning: false,
@@ -206,7 +219,7 @@ export async function discoverVLLMModels(baseUrl: string, apiKey?: string): Prom
206
219
  * @param apiKey - Optional API key (unused for LM Studio SDK)
207
220
  * @returns Array of discovered models
208
221
  */
209
- export async function discoverLMStudioModels(baseUrl: string, _apiKey?: string): Promise<Model<any>[]> {
222
+ export async function discoverLMStudioModels(baseUrl: string, _apiKey?: string): Promise<Model<Api>[]> {
210
223
  try {
211
224
  // Extract host and port from baseUrl
212
225
  const url = new URL(baseUrl);
@@ -226,10 +239,10 @@ export async function discoverLMStudioModels(baseUrl: string, _apiKey?: string):
226
239
  // Use 10x context length like Ollama does
227
240
  const maxTokens = contextWindow;
228
241
 
229
- const lmStudioModel: Model<any> = {
242
+ const lmStudioModel: Model<Api> = {
230
243
  id: model.path,
231
244
  name: model.displayName || model.path,
232
- api: "openai-completions" as any,
245
+ api: "openai-completions",
233
246
  provider: "", // Will be set by caller
234
247
  baseUrl: `${baseUrl}/v1`,
235
248
  reasoning: model.trainedForToolUse || false,
@@ -263,7 +276,7 @@ export async function discoverModels(
263
276
  type: "ollama" | "llama.cpp" | "vllm" | "lmstudio",
264
277
  baseUrl: string,
265
278
  apiKey?: string,
266
- ): Promise<Model<any>[]> {
279
+ ): Promise<Model<Api>[]> {
267
280
  switch (type) {
268
281
  case "ollama":
269
282
  return discoverOllamaModels(baseUrl, apiKey);
@@ -125,7 +125,7 @@ export function isCorsError(error: unknown): boolean {
125
125
  * @returns A streamFn compatible with Agent's streamFn option
126
126
  */
127
127
  export function createStreamFn(getProxyUrl: () => Promise<string | undefined>) {
128
- return async (model: Model<any>, context: Context, options?: SimpleStreamOptions) => {
128
+ return async (model: Model<Api>, context: Context, options?: SimpleStreamOptions) => {
129
129
  const apiKey = options?.apiKey;
130
130
  const proxyUrl = await getProxyUrl();
131
131