@llm4ts/core 0.1.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 (284) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +47 -0
  3. package/dist/AgentSession.d.ts +66 -0
  4. package/dist/AgentSession.d.ts.map +1 -0
  5. package/dist/AgentSession.js +53 -0
  6. package/dist/AgentSession.js.map +1 -0
  7. package/dist/Capability.d.ts +101 -0
  8. package/dist/Capability.d.ts.map +1 -0
  9. package/dist/Capability.js +210 -0
  10. package/dist/Capability.js.map +1 -0
  11. package/dist/Connector.d.ts +69 -0
  12. package/dist/Connector.d.ts.map +1 -0
  13. package/dist/Connector.js +86 -0
  14. package/dist/Connector.js.map +1 -0
  15. package/dist/ConnectorConfig.d.ts +70 -0
  16. package/dist/ConnectorConfig.d.ts.map +1 -0
  17. package/dist/ConnectorConfig.js +110 -0
  18. package/dist/ConnectorConfig.js.map +1 -0
  19. package/dist/ConnectorRegistry.d.ts +28 -0
  20. package/dist/ConnectorRegistry.d.ts.map +1 -0
  21. package/dist/ConnectorRegistry.js +87 -0
  22. package/dist/ConnectorRegistry.js.map +1 -0
  23. package/dist/ContextManagement.d.ts +78 -0
  24. package/dist/ContextManagement.d.ts.map +1 -0
  25. package/dist/ContextManagement.js +221 -0
  26. package/dist/ContextManagement.js.map +1 -0
  27. package/dist/Conversation.d.ts +18 -0
  28. package/dist/Conversation.d.ts.map +1 -0
  29. package/dist/Conversation.js +17 -0
  30. package/dist/Conversation.js.map +1 -0
  31. package/dist/Errors.d.ts +64 -0
  32. package/dist/Errors.d.ts.map +1 -0
  33. package/dist/Errors.js +76 -0
  34. package/dist/Errors.js.map +1 -0
  35. package/dist/HttpClient.d.ts +43 -0
  36. package/dist/HttpClient.d.ts.map +1 -0
  37. package/dist/HttpClient.js +99 -0
  38. package/dist/HttpClient.js.map +1 -0
  39. package/dist/LlmService.d.ts +24 -0
  40. package/dist/LlmService.d.ts.map +1 -0
  41. package/dist/LlmService.js +4 -0
  42. package/dist/LlmService.js.map +1 -0
  43. package/dist/Models.d.ts +138 -0
  44. package/dist/Models.d.ts.map +1 -0
  45. package/dist/Models.js +209 -0
  46. package/dist/Models.js.map +1 -0
  47. package/dist/ProcessExecutor.d.ts +57 -0
  48. package/dist/ProcessExecutor.d.ts.map +1 -0
  49. package/dist/ProcessExecutor.js +71 -0
  50. package/dist/ProcessExecutor.js.map +1 -0
  51. package/dist/RateLimiter.d.ts +44 -0
  52. package/dist/RateLimiter.d.ts.map +1 -0
  53. package/dist/RateLimiter.js +155 -0
  54. package/dist/RateLimiter.js.map +1 -0
  55. package/dist/Streaming.d.ts +17 -0
  56. package/dist/Streaming.d.ts.map +1 -0
  57. package/dist/Streaming.js +65 -0
  58. package/dist/Streaming.js.map +1 -0
  59. package/dist/StructuredOutput.d.ts +10 -0
  60. package/dist/StructuredOutput.d.ts.map +1 -0
  61. package/dist/StructuredOutput.js +97 -0
  62. package/dist/StructuredOutput.js.map +1 -0
  63. package/dist/TemporaryFiles.d.ts +20 -0
  64. package/dist/TemporaryFiles.d.ts.map +1 -0
  65. package/dist/TemporaryFiles.js +18 -0
  66. package/dist/TemporaryFiles.js.map +1 -0
  67. package/dist/UsageLimits.d.ts +5 -0
  68. package/dist/UsageLimits.d.ts.map +1 -0
  69. package/dist/UsageLimits.js +187 -0
  70. package/dist/UsageLimits.js.map +1 -0
  71. package/dist/eval/Checks.d.ts +6 -0
  72. package/dist/eval/Checks.d.ts.map +1 -0
  73. package/dist/eval/Checks.js +39 -0
  74. package/dist/eval/Checks.js.map +1 -0
  75. package/dist/eval/Eval.d.ts +57 -0
  76. package/dist/eval/Eval.d.ts.map +1 -0
  77. package/dist/eval/Eval.js +91 -0
  78. package/dist/eval/Eval.js.map +1 -0
  79. package/dist/eval/EvalSuite.d.ts +21 -0
  80. package/dist/eval/EvalSuite.d.ts.map +1 -0
  81. package/dist/eval/EvalSuite.js +30 -0
  82. package/dist/eval/EvalSuite.js.map +1 -0
  83. package/dist/eval/Evaluator.d.ts +10 -0
  84. package/dist/eval/Evaluator.d.ts.map +1 -0
  85. package/dist/eval/Evaluator.js +14 -0
  86. package/dist/eval/Evaluator.js.map +1 -0
  87. package/dist/eval/Judge.d.ts +15 -0
  88. package/dist/eval/Judge.d.ts.map +1 -0
  89. package/dist/eval/Judge.js +73 -0
  90. package/dist/eval/Judge.js.map +1 -0
  91. package/dist/observability/MeteredLlmService.d.ts +16 -0
  92. package/dist/observability/MeteredLlmService.d.ts.map +1 -0
  93. package/dist/observability/MeteredLlmService.js +66 -0
  94. package/dist/observability/MeteredLlmService.js.map +1 -0
  95. package/dist/observability/Metrics.d.ts +71 -0
  96. package/dist/observability/Metrics.d.ts.map +1 -0
  97. package/dist/observability/Metrics.js +147 -0
  98. package/dist/observability/Metrics.js.map +1 -0
  99. package/dist/observability/Redaction.d.ts +8 -0
  100. package/dist/observability/Redaction.d.ts.map +1 -0
  101. package/dist/observability/Redaction.js +38 -0
  102. package/dist/observability/Redaction.js.map +1 -0
  103. package/dist/observability/StreamRecorder.d.ts +9 -0
  104. package/dist/observability/StreamRecorder.d.ts.map +1 -0
  105. package/dist/observability/StreamRecorder.js +10 -0
  106. package/dist/observability/StreamRecorder.js.map +1 -0
  107. package/dist/observability/StructuredLogger.d.ts +31 -0
  108. package/dist/observability/StructuredLogger.d.ts.map +1 -0
  109. package/dist/observability/StructuredLogger.js +34 -0
  110. package/dist/observability/StructuredLogger.js.map +1 -0
  111. package/dist/observability/Tracing.d.ts +38 -0
  112. package/dist/observability/Tracing.d.ts.map +1 -0
  113. package/dist/observability/Tracing.js +75 -0
  114. package/dist/observability/Tracing.js.map +1 -0
  115. package/dist/providers/AnthropicModels.d.ts +95 -0
  116. package/dist/providers/AnthropicModels.d.ts.map +1 -0
  117. package/dist/providers/AnthropicModels.js +83 -0
  118. package/dist/providers/AnthropicModels.js.map +1 -0
  119. package/dist/providers/AnthropicProvider.d.ts +13 -0
  120. package/dist/providers/AnthropicProvider.d.ts.map +1 -0
  121. package/dist/providers/AnthropicProvider.js +177 -0
  122. package/dist/providers/AnthropicProvider.js.map +1 -0
  123. package/dist/providers/AntigravityConnector.d.ts +7 -0
  124. package/dist/providers/AntigravityConnector.d.ts.map +1 -0
  125. package/dist/providers/AntigravityConnector.js +52 -0
  126. package/dist/providers/AntigravityConnector.js.map +1 -0
  127. package/dist/providers/ClaudeAgentSession.d.ts +11 -0
  128. package/dist/providers/ClaudeAgentSession.d.ts.map +1 -0
  129. package/dist/providers/ClaudeAgentSession.js +158 -0
  130. package/dist/providers/ClaudeAgentSession.js.map +1 -0
  131. package/dist/providers/ClaudeCliConnector.d.ts +9 -0
  132. package/dist/providers/ClaudeCliConnector.d.ts.map +1 -0
  133. package/dist/providers/ClaudeCliConnector.js +123 -0
  134. package/dist/providers/ClaudeCliConnector.js.map +1 -0
  135. package/dist/providers/CliSupport.d.ts +21 -0
  136. package/dist/providers/CliSupport.d.ts.map +1 -0
  137. package/dist/providers/CliSupport.js +62 -0
  138. package/dist/providers/CliSupport.js.map +1 -0
  139. package/dist/providers/CodexConnector.d.ts +12 -0
  140. package/dist/providers/CodexConnector.d.ts.map +1 -0
  141. package/dist/providers/CodexConnector.js +175 -0
  142. package/dist/providers/CodexConnector.js.map +1 -0
  143. package/dist/providers/ConnectorFactories.d.ts +15 -0
  144. package/dist/providers/ConnectorFactories.d.ts.map +1 -0
  145. package/dist/providers/ConnectorFactories.js +74 -0
  146. package/dist/providers/ConnectorFactories.js.map +1 -0
  147. package/dist/providers/CopilotConnector.d.ts +6 -0
  148. package/dist/providers/CopilotConnector.d.ts.map +1 -0
  149. package/dist/providers/CopilotConnector.js +35 -0
  150. package/dist/providers/CopilotConnector.js.map +1 -0
  151. package/dist/providers/CursorConnector.d.ts +8 -0
  152. package/dist/providers/CursorConnector.d.ts.map +1 -0
  153. package/dist/providers/CursorConnector.js +82 -0
  154. package/dist/providers/CursorConnector.js.map +1 -0
  155. package/dist/providers/GeminiApiProvider.d.ts +9 -0
  156. package/dist/providers/GeminiApiProvider.d.ts.map +1 -0
  157. package/dist/providers/GeminiApiProvider.js +181 -0
  158. package/dist/providers/GeminiApiProvider.js.map +1 -0
  159. package/dist/providers/GeminiCliProvider.d.ts +106 -0
  160. package/dist/providers/GeminiCliProvider.d.ts.map +1 -0
  161. package/dist/providers/GeminiCliProvider.js +596 -0
  162. package/dist/providers/GeminiCliProvider.js.map +1 -0
  163. package/dist/providers/GeminiModels.d.ts +92 -0
  164. package/dist/providers/GeminiModels.d.ts.map +1 -0
  165. package/dist/providers/GeminiModels.js +80 -0
  166. package/dist/providers/GeminiModels.js.map +1 -0
  167. package/dist/providers/GrokCliConnector.d.ts +8 -0
  168. package/dist/providers/GrokCliConnector.d.ts.map +1 -0
  169. package/dist/providers/GrokCliConnector.js +82 -0
  170. package/dist/providers/GrokCliConnector.js.map +1 -0
  171. package/dist/providers/LmStudioModels.d.ts +114 -0
  172. package/dist/providers/LmStudioModels.d.ts.map +1 -0
  173. package/dist/providers/LmStudioModels.js +102 -0
  174. package/dist/providers/LmStudioModels.js.map +1 -0
  175. package/dist/providers/LmStudioProvider.d.ts +13 -0
  176. package/dist/providers/LmStudioProvider.d.ts.map +1 -0
  177. package/dist/providers/LmStudioProvider.js +158 -0
  178. package/dist/providers/LmStudioProvider.js.map +1 -0
  179. package/dist/providers/MockProvider.d.ts +6 -0
  180. package/dist/providers/MockProvider.d.ts.map +1 -0
  181. package/dist/providers/MockProvider.js +271 -0
  182. package/dist/providers/MockProvider.js.map +1 -0
  183. package/dist/providers/OllamaModels.d.ts +68 -0
  184. package/dist/providers/OllamaModels.d.ts.map +1 -0
  185. package/dist/providers/OllamaModels.js +59 -0
  186. package/dist/providers/OllamaModels.js.map +1 -0
  187. package/dist/providers/OllamaProvider.d.ts +7 -0
  188. package/dist/providers/OllamaProvider.d.ts.map +1 -0
  189. package/dist/providers/OllamaProvider.js +134 -0
  190. package/dist/providers/OllamaProvider.js.map +1 -0
  191. package/dist/providers/OpenAIModels.d.ts +139 -0
  192. package/dist/providers/OpenAIModels.d.ts.map +1 -0
  193. package/dist/providers/OpenAIModels.js +123 -0
  194. package/dist/providers/OpenAIModels.js.map +1 -0
  195. package/dist/providers/OpenAIProvider.d.ts +9 -0
  196. package/dist/providers/OpenAIProvider.d.ts.map +1 -0
  197. package/dist/providers/OpenAIProvider.js +185 -0
  198. package/dist/providers/OpenAIProvider.js.map +1 -0
  199. package/dist/providers/OpenCodeCliConnector.d.ts +8 -0
  200. package/dist/providers/OpenCodeCliConnector.d.ts.map +1 -0
  201. package/dist/providers/OpenCodeCliConnector.js +93 -0
  202. package/dist/providers/OpenCodeCliConnector.js.map +1 -0
  203. package/dist/providers/OpenCodeModels.d.ts +61 -0
  204. package/dist/providers/OpenCodeModels.d.ts.map +1 -0
  205. package/dist/providers/OpenCodeModels.js +53 -0
  206. package/dist/providers/OpenCodeModels.js.map +1 -0
  207. package/dist/providers/OpenCodeProvider.d.ts +10 -0
  208. package/dist/providers/OpenCodeProvider.d.ts.map +1 -0
  209. package/dist/providers/OpenCodeProvider.js +154 -0
  210. package/dist/providers/OpenCodeProvider.js.map +1 -0
  211. package/dist/providers/PiConnector.d.ts +8 -0
  212. package/dist/providers/PiConnector.d.ts.map +1 -0
  213. package/dist/providers/PiConnector.js +99 -0
  214. package/dist/providers/PiConnector.js.map +1 -0
  215. package/dist/tools/Tool.d.ts +83 -0
  216. package/dist/tools/Tool.d.ts.map +1 -0
  217. package/dist/tools/Tool.js +278 -0
  218. package/dist/tools/Tool.js.map +1 -0
  219. package/dist/tools/ToolCallingExecutor.d.ts +18 -0
  220. package/dist/tools/ToolCallingExecutor.d.ts.map +1 -0
  221. package/dist/tools/ToolCallingExecutor.js +64 -0
  222. package/dist/tools/ToolCallingExecutor.js.map +1 -0
  223. package/dist/tools/ToolRegistry.d.ts +45 -0
  224. package/dist/tools/ToolRegistry.d.ts.map +1 -0
  225. package/dist/tools/ToolRegistry.js +133 -0
  226. package/dist/tools/ToolRegistry.js.map +1 -0
  227. package/package.json +105 -0
  228. package/src/.gitkeep +1 -0
  229. package/src/AgentSession.ts +69 -0
  230. package/src/Capability.ts +240 -0
  231. package/src/Connector.ts +220 -0
  232. package/src/ConnectorConfig.ts +146 -0
  233. package/src/ConnectorRegistry.ts +184 -0
  234. package/src/ContextManagement.ts +372 -0
  235. package/src/Conversation.ts +21 -0
  236. package/src/Errors.ts +87 -0
  237. package/src/HttpClient.ts +202 -0
  238. package/src/LlmService.ts +45 -0
  239. package/src/Models.ts +250 -0
  240. package/src/ProcessExecutor.ts +157 -0
  241. package/src/RateLimiter.ts +238 -0
  242. package/src/Streaming.ts +164 -0
  243. package/src/StructuredOutput.ts +138 -0
  244. package/src/TemporaryFiles.ts +51 -0
  245. package/src/UsageLimits.ts +242 -0
  246. package/src/eval/Checks.ts +68 -0
  247. package/src/eval/Eval.ts +112 -0
  248. package/src/eval/EvalSuite.ts +63 -0
  249. package/src/eval/Evaluator.ts +31 -0
  250. package/src/eval/Judge.ts +92 -0
  251. package/src/observability/MeteredLlmService.ts +156 -0
  252. package/src/observability/Metrics.ts +195 -0
  253. package/src/observability/Redaction.ts +55 -0
  254. package/src/observability/StreamRecorder.ts +32 -0
  255. package/src/observability/StructuredLogger.ts +62 -0
  256. package/src/observability/Tracing.ts +141 -0
  257. package/src/providers/AnthropicModels.ts +96 -0
  258. package/src/providers/AnthropicProvider.ts +320 -0
  259. package/src/providers/AntigravityConnector.ts +77 -0
  260. package/src/providers/ClaudeAgentSession.ts +226 -0
  261. package/src/providers/ClaudeCliConnector.ts +201 -0
  262. package/src/providers/CliSupport.ts +100 -0
  263. package/src/providers/CodexConnector.ts +251 -0
  264. package/src/providers/ConnectorFactories.ts +142 -0
  265. package/src/providers/CopilotConnector.ts +47 -0
  266. package/src/providers/CursorConnector.ts +120 -0
  267. package/src/providers/GeminiApiProvider.ts +337 -0
  268. package/src/providers/GeminiCliProvider.ts +793 -0
  269. package/src/providers/GeminiModels.ts +97 -0
  270. package/src/providers/GrokCliConnector.ts +117 -0
  271. package/src/providers/LmStudioModels.ts +117 -0
  272. package/src/providers/LmStudioProvider.ts +260 -0
  273. package/src/providers/MockProvider.ts +322 -0
  274. package/src/providers/OllamaModels.ts +64 -0
  275. package/src/providers/OllamaProvider.ts +236 -0
  276. package/src/providers/OpenAIModels.ts +152 -0
  277. package/src/providers/OpenAIProvider.ts +331 -0
  278. package/src/providers/OpenCodeCliConnector.ts +135 -0
  279. package/src/providers/OpenCodeModels.ts +60 -0
  280. package/src/providers/OpenCodeProvider.ts +278 -0
  281. package/src/providers/PiConnector.ts +142 -0
  282. package/src/tools/Tool.ts +379 -0
  283. package/src/tools/ToolCallingExecutor.ts +106 -0
  284. package/src/tools/ToolRegistry.ts +224 -0
@@ -0,0 +1,97 @@
1
+ import * as Effect from "effect/Effect"
2
+ import * as Schema from "effect/Schema"
3
+ import { JsonSchema } from "../Models.ts"
4
+
5
+ const empty = <A>(): ReadonlyArray<A> => Object.freeze([])
6
+
7
+ const arrayWithDefaults = <A extends Schema.Top>(item: A) =>
8
+ Schema.Array(item).pipe(
9
+ Schema.withConstructorDefault(Effect.succeed(empty<typeof item.Type>())),
10
+ Schema.withDecodingDefault(Effect.succeed(empty<typeof item.Encoded>()))
11
+ )
12
+
13
+ export class GeminiGenerationConfig extends Schema.Class<GeminiGenerationConfig>(
14
+ "GeminiGenerationConfig"
15
+ )({
16
+ responseMimeType: Schema.optionalKey(Schema.String),
17
+ responseSchema: Schema.optionalKey(JsonSchema)
18
+ }) {}
19
+
20
+ export class GeminiPart extends Schema.Class<GeminiPart>("GeminiPart")({
21
+ text: Schema.optionalKey(Schema.NullOr(Schema.String))
22
+ }) {}
23
+
24
+ export class GeminiContent extends Schema.Class<GeminiContent>("GeminiContent")({
25
+ parts: Schema.Array(GeminiPart)
26
+ }) {}
27
+
28
+ export class GeminiGenerateContentRequest extends Schema.Class<GeminiGenerateContentRequest>(
29
+ "GeminiGenerateContentRequest"
30
+ )({
31
+ contents: Schema.Array(GeminiContent),
32
+ generationConfig: Schema.optionalKey(GeminiGenerationConfig)
33
+ }) {}
34
+
35
+ export class GeminiUsageMetadata extends Schema.Class<GeminiUsageMetadata>("GeminiUsageMetadata")({
36
+ promptTokenCount: Schema.optionalKey(Schema.Int),
37
+ candidatesTokenCount: Schema.optionalKey(Schema.Int),
38
+ totalTokenCount: Schema.optionalKey(Schema.Int)
39
+ }) {}
40
+
41
+ export class GeminiCandidate extends Schema.Class<GeminiCandidate>("GeminiCandidate")({
42
+ content: GeminiContent,
43
+ finishReason: Schema.optionalKey(Schema.NullOr(Schema.String))
44
+ }) {}
45
+
46
+ export class GeminiGenerateContentResponse extends Schema.Class<GeminiGenerateContentResponse>(
47
+ "GeminiGenerateContentResponse"
48
+ )({
49
+ candidates: arrayWithDefaults(GeminiCandidate),
50
+ usageMetadata: Schema.optionalKey(GeminiUsageMetadata)
51
+ }) {}
52
+
53
+ export class GeminiFunctionDeclaration extends Schema.Class<GeminiFunctionDeclaration>(
54
+ "GeminiFunctionDeclaration"
55
+ )({
56
+ name: Schema.String,
57
+ description: Schema.String,
58
+ parameters: JsonSchema
59
+ }) {}
60
+
61
+ export class GeminiToolDef extends Schema.Class<GeminiToolDef>("GeminiToolDef")({
62
+ functionDeclarations: Schema.Array(GeminiFunctionDeclaration)
63
+ }) {}
64
+
65
+ export class GeminiFunctionCall extends Schema.Class<GeminiFunctionCall>("GeminiFunctionCall")({
66
+ name: Schema.String,
67
+ args: Schema.Json
68
+ }) {}
69
+
70
+ export class GeminiPartFull extends Schema.Class<GeminiPartFull>("GeminiPartFull")({
71
+ text: Schema.optionalKey(Schema.NullOr(Schema.String)),
72
+ functionCall: Schema.optionalKey(GeminiFunctionCall)
73
+ }) {}
74
+
75
+ export class GeminiContentFull extends Schema.Class<GeminiContentFull>("GeminiContentFull")({
76
+ parts: Schema.Array(GeminiPartFull)
77
+ }) {}
78
+
79
+ export class GeminiCandidateFull extends Schema.Class<GeminiCandidateFull>("GeminiCandidateFull")({
80
+ content: GeminiContentFull,
81
+ finishReason: Schema.optionalKey(Schema.NullOr(Schema.String))
82
+ }) {}
83
+
84
+ export class GeminiGenerateContentResponseFull extends Schema.Class<GeminiGenerateContentResponseFull>(
85
+ "GeminiGenerateContentResponseFull"
86
+ )({
87
+ candidates: arrayWithDefaults(GeminiCandidateFull),
88
+ usageMetadata: Schema.optionalKey(GeminiUsageMetadata)
89
+ }) {}
90
+
91
+ export class GeminiGenerateContentRequestWithTools extends Schema.Class<GeminiGenerateContentRequestWithTools>(
92
+ "GeminiGenerateContentRequestWithTools"
93
+ )({
94
+ contents: Schema.Array(GeminiContent),
95
+ tools: Schema.Array(GeminiToolDef),
96
+ generationConfig: Schema.optionalKey(GeminiGenerationConfig)
97
+ }) {}
@@ -0,0 +1,117 @@
1
+ import * as Effect from "effect/Effect"
2
+ import * as Stream from "effect/Stream"
3
+ import { makeCliConnector, type CliConnectorShape } from "../Connector.ts"
4
+ import type { CliConnectorConfig } from "../ConnectorConfig.ts"
5
+ import { ConnectorIds, LlmChunk, TokenUsage } from "../Models.ts"
6
+ import type { ProcessExecutorShape } from "../ProcessExecutor.ts"
7
+ import {
8
+ failClassifiedCliError,
9
+ jsonField,
10
+ jsonIntField,
11
+ jsonObjectEntries,
12
+ jsonStringField,
13
+ optionalModelArgs,
14
+ parseJsonLine,
15
+ sortedFlagArgs,
16
+ usageEventChunk
17
+ } from "./CliSupport.ts"
18
+
19
+ export const grokCliExtraArgs = (config: CliConnectorConfig): ReadonlyArray<string> => [
20
+ ...optionalModelArgs(config.model),
21
+ ...(config.readOnly ? ["--permission-mode", "plan"] : ["--always-approve"]),
22
+ ...sortedFlagArgs(config.flags)
23
+ ]
24
+
25
+ export const parseGrokCliStreamLine = (line: string): ReadonlyArray<LlmChunk> => {
26
+ const json = parseJsonLine(line)
27
+ if (json === undefined) {
28
+ return []
29
+ }
30
+
31
+ switch (jsonStringField(json, "type")) {
32
+ case "text": {
33
+ const text = jsonStringField(json, "data")
34
+ return text === undefined || text.length === 0 ? [] : [LlmChunk.make({ delta: text })]
35
+ }
36
+ case "end": {
37
+ const usage = jsonField(json, "usage")
38
+ if (usage === undefined) {
39
+ return []
40
+ }
41
+ const prompt = jsonIntField(usage, "input_tokens") ?? 0
42
+ const completion = jsonIntField(usage, "output_tokens") ?? 0
43
+ const cached = jsonIntField(usage, "cache_read_input_tokens")
44
+ const model = jsonObjectEntries(jsonField(json, "modelUsage"))[0]?.[0]
45
+ return [
46
+ usageEventChunk(
47
+ model,
48
+ TokenUsage.make({
49
+ prompt,
50
+ completion,
51
+ total: jsonIntField(usage, "total_tokens") ?? prompt + completion,
52
+ ...(cached === undefined ? {} : { cached })
53
+ })
54
+ )
55
+ ]
56
+ }
57
+ case "error":
58
+ return [
59
+ LlmChunk.make({
60
+ delta: "",
61
+ metadata: {
62
+ grokError:
63
+ jsonStringField(json, "data") ?? jsonStringField(json, "message") ?? "grok error"
64
+ }
65
+ })
66
+ ]
67
+ default:
68
+ return []
69
+ }
70
+ }
71
+
72
+ export const makeGrokCliConnector = (
73
+ config: CliConnectorConfig,
74
+ executor: ProcessExecutorShape
75
+ ): CliConnectorShape => {
76
+ const cwd = config.workingDir ?? "."
77
+ const extraArgs = grokCliExtraArgs(config)
78
+ const buildArgv = (prompt: string): ReadonlyArray<string> => ["grok", ...extraArgs, "-p", prompt]
79
+
80
+ const complete = Effect.fn("@llm4ts/core/providers/GrokCliConnector.complete")(function* (
81
+ prompt: string
82
+ ) {
83
+ const result = yield* executor.run(buildArgv(prompt), cwd, config.envVars)
84
+ if (result.exitCode !== 0) {
85
+ const raw = result.stdout.join("\n")
86
+ return yield* failClassifiedCliError("grok", `grok exited with code ${result.exitCode}`, raw)
87
+ }
88
+ return result.stdout.join("\n")
89
+ })
90
+
91
+ const completeStream = (prompt: string) =>
92
+ executor
93
+ .runStreaming(
94
+ ["grok", "--output-format", "streaming-json", ...extraArgs, "-p", prompt],
95
+ cwd,
96
+ config.envVars
97
+ )
98
+ .pipe(
99
+ Stream.flatMap((line) => Stream.fromIterable(parseGrokCliStreamLine(line))),
100
+ Stream.mapEffect((chunk) => {
101
+ const message = chunk.metadata.grokError
102
+ return message === undefined
103
+ ? Effect.succeed(chunk)
104
+ : failClassifiedCliError("grok", "grok error", message)
105
+ })
106
+ )
107
+
108
+ return makeCliConnector({
109
+ id: ConnectorIds.Grok,
110
+ interactionSupport: "ContinuationOnly",
111
+ buildArgv: (prompt, _context) => buildArgv(prompt),
112
+ buildInteractiveArgv: (_context) => ["grok", ...extraArgs],
113
+ complete,
114
+ completeStream,
115
+ versionProbe: { executor, binary: "grok", cwd }
116
+ })
117
+ }
@@ -0,0 +1,117 @@
1
+ import * as Schema from "effect/Schema"
2
+
3
+ export class LmStudioInput extends Schema.Class<LmStudioInput>("LmStudioInput")({
4
+ type: Schema.String,
5
+ content: Schema.optionalKey(Schema.String),
6
+ data_url: Schema.optionalKey(Schema.String)
7
+ }) {}
8
+
9
+ export const LmStudioInputPayload = Schema.Union([Schema.String, Schema.Array(LmStudioInput)])
10
+ export type LmStudioInputPayload = typeof LmStudioInputPayload.Type
11
+
12
+ export class LmStudioPluginIntegration extends Schema.Class<LmStudioPluginIntegration>(
13
+ "LmStudioPluginIntegration"
14
+ )({
15
+ type: Schema.String,
16
+ id: Schema.String,
17
+ allowed_tools: Schema.optionalKey(Schema.Array(Schema.String))
18
+ }) {}
19
+
20
+ export class LmStudioEphemeralMcpIntegration extends Schema.Class<LmStudioEphemeralMcpIntegration>(
21
+ "LmStudioEphemeralMcpIntegration"
22
+ )({
23
+ type: Schema.String,
24
+ server_label: Schema.String,
25
+ server_url: Schema.String,
26
+ allowed_tools: Schema.optionalKey(Schema.Array(Schema.String)),
27
+ headers: Schema.optionalKey(Schema.Record(Schema.String, Schema.String))
28
+ }) {}
29
+
30
+ export const LmStudioIntegration = Schema.Union([
31
+ LmStudioPluginIntegration,
32
+ LmStudioEphemeralMcpIntegration
33
+ ])
34
+ export type LmStudioIntegration = typeof LmStudioIntegration.Type
35
+
36
+ export class LmStudioChatRequest extends Schema.Class<LmStudioChatRequest>("LmStudioChatRequest")({
37
+ model: Schema.String,
38
+ input: LmStudioInputPayload,
39
+ system_prompt: Schema.optionalKey(Schema.String),
40
+ integrations: Schema.optionalKey(Schema.Array(LmStudioIntegration)),
41
+ stream: Schema.optionalKey(Schema.Boolean),
42
+ temperature: Schema.optionalKey(Schema.Number),
43
+ top_p: Schema.optionalKey(Schema.Number),
44
+ top_k: Schema.optionalKey(Schema.Int),
45
+ min_p: Schema.optionalKey(Schema.Number),
46
+ repeat_penalty: Schema.optionalKey(Schema.Number),
47
+ max_output_tokens: Schema.optionalKey(Schema.Int),
48
+ reasoning: Schema.optionalKey(Schema.String),
49
+ context_length: Schema.optionalKey(Schema.Int),
50
+ store: Schema.optionalKey(Schema.Boolean),
51
+ previous_response_id: Schema.optionalKey(Schema.String)
52
+ }) {}
53
+
54
+ export class LmStudioMessage extends Schema.Class<LmStudioMessage>("LmStudioMessage")({
55
+ role: Schema.String,
56
+ content: Schema.String,
57
+ name: Schema.optionalKey(Schema.String)
58
+ }) {}
59
+
60
+ export class LmStudioOutputItem extends Schema.Class<LmStudioOutputItem>("LmStudioOutputItem")({
61
+ type: Schema.String,
62
+ content: Schema.optionalKey(Schema.NullOr(Schema.String)),
63
+ tool: Schema.optionalKey(Schema.String),
64
+ arguments: Schema.optionalKey(Schema.Json),
65
+ output: Schema.optionalKey(Schema.String),
66
+ provider_info: Schema.optionalKey(Schema.Json),
67
+ reason: Schema.optionalKey(Schema.String),
68
+ metadata: Schema.optionalKey(Schema.Json)
69
+ }) {}
70
+
71
+ export class LmStudioStats extends Schema.Class<LmStudioStats>("LmStudioStats")({
72
+ input_tokens: Schema.optionalKey(Schema.Int),
73
+ total_output_tokens: Schema.optionalKey(Schema.Int),
74
+ reasoning_output_tokens: Schema.optionalKey(Schema.Int),
75
+ tokens_per_second: Schema.optionalKey(Schema.Number),
76
+ time_to_first_token_seconds: Schema.optionalKey(Schema.Number),
77
+ model_load_time_seconds: Schema.optionalKey(Schema.Number)
78
+ }) {}
79
+
80
+ export class LmStudioChatResponse extends Schema.Class<LmStudioChatResponse>(
81
+ "LmStudioChatResponse"
82
+ )({
83
+ model_instance_id: Schema.String,
84
+ output: Schema.Array(LmStudioOutputItem),
85
+ stats: Schema.optionalKey(LmStudioStats),
86
+ response_id: Schema.optionalKey(Schema.String)
87
+ }) {}
88
+
89
+ export class LmStudioModel extends Schema.Class<LmStudioModel>("LmStudioModel")({
90
+ id: Schema.String,
91
+ path: Schema.String,
92
+ type: Schema.String,
93
+ loaded: Schema.Boolean,
94
+ architecture: Schema.optionalKey(Schema.String)
95
+ }) {}
96
+
97
+ export class LmStudioModelsResponse extends Schema.Class<LmStudioModelsResponse>(
98
+ "LmStudioModelsResponse"
99
+ )({
100
+ models: Schema.Array(LmStudioModel)
101
+ }) {}
102
+
103
+ export class LmStudioLoadModelRequest extends Schema.Class<LmStudioLoadModelRequest>(
104
+ "LmStudioLoadModelRequest"
105
+ )({
106
+ model: Schema.String,
107
+ context_length: Schema.optionalKey(Schema.Int),
108
+ gpu_layers: Schema.optionalKey(Schema.Int)
109
+ }) {}
110
+
111
+ export class LmStudioLoadModelResponse extends Schema.Class<LmStudioLoadModelResponse>(
112
+ "LmStudioLoadModelResponse"
113
+ )({
114
+ success: Schema.Boolean,
115
+ model: Schema.String,
116
+ message: Schema.optionalKey(Schema.String)
117
+ }) {}
@@ -0,0 +1,260 @@
1
+ import * as Effect from "effect/Effect"
2
+ import * as Redacted from "effect/Redacted"
3
+ import * as Schema from "effect/Schema"
4
+ import * as Stream from "effect/Stream"
5
+ import { makeApiConnector, type ApiConnectorShape } from "../Connector.ts"
6
+ import { ConfigError, InvalidRequestError, ParseError, type LlmError } from "../Errors.ts"
7
+ import type { HttpClientShape } from "../HttpClient.ts"
8
+ import type { StructuredResult } from "../LlmService.ts"
9
+ import {
10
+ ConnectorIds,
11
+ LlmChunk,
12
+ type JsonSchema,
13
+ type LlmConfig,
14
+ type Message,
15
+ type MessageRole
16
+ } from "../Models.ts"
17
+ import { parseFromText } from "../StructuredOutput.ts"
18
+ import { LmStudioChatRequest, LmStudioChatResponse, LmStudioMessage } from "./LmStudioModels.ts"
19
+ import { OpenAIChatChunk, OpenAIChatCompletionRequest, OpenAIChatMessage } from "./OpenAIModels.ts"
20
+
21
+ const emptyHeaders: Readonly<Record<string, string>> = Object.freeze({})
22
+
23
+ export const normalizeLmStudioBaseUrl = (raw: string): string => {
24
+ const trimmed = raw.trim().replace(/\/+$/, "")
25
+ return trimmed.endsWith("/v1") ? trimmed.slice(0, -"/v1".length) : trimmed
26
+ }
27
+
28
+ const roleName = (role: MessageRole): string => {
29
+ switch (role) {
30
+ case "System":
31
+ return "system"
32
+ case "User":
33
+ return "user"
34
+ case "Assistant":
35
+ return "assistant"
36
+ case "Tool":
37
+ return "tool"
38
+ }
39
+ }
40
+
41
+ export const lmStudioHistory = (messages: ReadonlyArray<Message>): ReadonlyArray<LmStudioMessage> =>
42
+ messages.map((message) =>
43
+ LmStudioMessage.make({
44
+ role: roleName(message.role),
45
+ content: message.content
46
+ })
47
+ )
48
+
49
+ export interface LmStudioNativeInput {
50
+ readonly input: string
51
+ readonly systemPrompt: string | undefined
52
+ }
53
+
54
+ export const renderLmStudioNativeInput = (
55
+ messages: ReadonlyArray<LmStudioMessage>
56
+ ): LmStudioNativeInput => {
57
+ const nonSystem = messages.filter((message) => message.role !== "system")
58
+ const inputMessages = nonSystem.length > 0 ? nonSystem : messages
59
+ const system = messages
60
+ .filter((message) => message.role === "system")
61
+ .map((message) => message.content.trim())
62
+ .filter((content) => content.length > 0)
63
+
64
+ return {
65
+ input: inputMessages.map((message) => `${message.role}: ${message.content}`).join("\n"),
66
+ systemPrompt: system.length === 0 ? undefined : system.join("\n")
67
+ }
68
+ }
69
+
70
+ const decodeNativeResponse = (raw: string): Effect.Effect<LmStudioChatResponse, ParseError> =>
71
+ Schema.decodeUnknownEffect(Schema.fromJsonString(LmStudioChatResponse))(raw).pipe(
72
+ Effect.mapError((error) =>
73
+ ParseError.make({
74
+ message: `Failed to decode LmStudio native API response: ${String(error)}`,
75
+ raw
76
+ })
77
+ )
78
+ )
79
+
80
+ const decodeStreamChunk = (raw: string): Effect.Effect<OpenAIChatChunk, ParseError> =>
81
+ Schema.decodeUnknownEffect(Schema.fromJsonString(OpenAIChatChunk))(raw).pipe(
82
+ Effect.mapError((error) =>
83
+ ParseError.make({
84
+ message: `Failed to parse LmStudio stream chunk: ${String(error)}`,
85
+ raw
86
+ })
87
+ )
88
+ )
89
+
90
+ const nativeContent = (
91
+ response: LmStudioChatResponse,
92
+ raw: string
93
+ ): Effect.Effect<string, ParseError> => {
94
+ const content = response.output
95
+ .find(
96
+ (item) =>
97
+ item.type === "message" &&
98
+ item.content !== undefined &&
99
+ item.content !== null &&
100
+ item.content.trim().length > 0
101
+ )
102
+ ?.content?.trim()
103
+
104
+ return content === undefined || content === null
105
+ ? Effect.fail(
106
+ ParseError.make({
107
+ message: "LmStudio response missing choices[0].message.content",
108
+ raw
109
+ })
110
+ )
111
+ : Effect.succeed(content)
112
+ }
113
+
114
+ export const makeLmStudioProvider = (
115
+ config: LlmConfig,
116
+ httpClient: HttpClientShape
117
+ ): ApiConnectorShape => {
118
+ const baseUrl: Effect.Effect<string, ConfigError> =
119
+ config.baseUrl === undefined
120
+ ? Effect.fail(
121
+ ConfigError.make({
122
+ message: "Missing baseUrl for LmStudio provider"
123
+ })
124
+ )
125
+ : Effect.succeed(normalizeLmStudioBaseUrl(config.baseUrl))
126
+
127
+ const authHeaders = (): Readonly<Record<string, string>> =>
128
+ config.apiKey === undefined
129
+ ? emptyHeaders
130
+ : {
131
+ Authorization: `Bearer ${Redacted.value(config.apiKey)}`
132
+ }
133
+
134
+ const streamRequest = (
135
+ messages: ReadonlyArray<LmStudioMessage>
136
+ ): Stream.Stream<LlmChunk, LlmError> =>
137
+ Stream.unwrap(
138
+ Effect.map(baseUrl, (normalized) => {
139
+ const request = OpenAIChatCompletionRequest.make({
140
+ model: config.model,
141
+ messages: messages.map((message) =>
142
+ OpenAIChatMessage.make({
143
+ role: message.role,
144
+ content: message.content
145
+ })
146
+ ),
147
+ temperature: config.temperature ?? 0.7,
148
+ stream: true,
149
+ ...(config.maxTokens === undefined ? {} : { max_tokens: config.maxTokens })
150
+ })
151
+ return httpClient
152
+ .postJsonStreamSSE(
153
+ `${normalized}/v1/chat/completions`,
154
+ JSON.stringify(request),
155
+ authHeaders(),
156
+ config.timeout
157
+ )
158
+ .pipe(
159
+ Stream.mapEffect(decodeStreamChunk),
160
+ Stream.flatMap((chunk) => {
161
+ const choice = chunk.choices[0]
162
+ const delta = choice?.delta?.content ?? ""
163
+ const finishReason = choice?.finish_reason ?? undefined
164
+ return delta.length > 0 || finishReason !== undefined
165
+ ? Stream.succeed(
166
+ LlmChunk.make({
167
+ delta,
168
+ metadata: {
169
+ provider: "lmstudio",
170
+ model: config.model
171
+ },
172
+ ...(finishReason === undefined ? {} : { finishReason })
173
+ })
174
+ )
175
+ : Stream.empty
176
+ })
177
+ )
178
+ })
179
+ )
180
+
181
+ const nativeRequest = (
182
+ messages: ReadonlyArray<LmStudioMessage>
183
+ ): Effect.Effect<readonly [response: LmStudioChatResponse, raw: string], LlmError> =>
184
+ Effect.gen(function* () {
185
+ const normalized = yield* baseUrl
186
+ const rendered = renderLmStudioNativeInput(messages)
187
+ const request = LmStudioChatRequest.make({
188
+ model: config.model,
189
+ input: rendered.input,
190
+ temperature: config.temperature ?? 0.7,
191
+ stream: false,
192
+ ...(rendered.systemPrompt === undefined ? {} : { system_prompt: rendered.systemPrompt }),
193
+ ...(config.maxTokens === undefined ? {} : { max_output_tokens: config.maxTokens })
194
+ })
195
+ const raw = yield* httpClient.postJson(
196
+ `${normalized}/api/v1/chat`,
197
+ JSON.stringify(request),
198
+ authHeaders(),
199
+ config.timeout
200
+ )
201
+ const response = yield* decodeNativeResponse(raw)
202
+ return [response, raw]
203
+ })
204
+
205
+ const executeStructuredWithUsage = <A, E, RD, RE>(
206
+ prompt: string,
207
+ schema: Schema.ConstraintCodec<A, E, RD, RE>,
208
+ jsonSchema: JsonSchema
209
+ ): Effect.Effect<StructuredResult<A>, LlmError, RD> =>
210
+ Effect.gen(function* () {
211
+ const [response, raw] = yield* nativeRequest([
212
+ LmStudioMessage.make({
213
+ role: "user",
214
+ content:
215
+ `${prompt}\n\n` +
216
+ "Please respond with valid JSON only, no additional text or markdown formatting."
217
+ })
218
+ ])
219
+ const content = yield* nativeContent(response, raw)
220
+ const value = yield* parseFromText(content, schema, jsonSchema)
221
+ const result: StructuredResult<A> = [value, undefined, undefined]
222
+ return result
223
+ })
224
+
225
+ const isAvailable: Effect.Effect<boolean> =
226
+ config.baseUrl === undefined
227
+ ? Effect.succeed(false)
228
+ : httpClient
229
+ .get(
230
+ `${normalizeLmStudioBaseUrl(config.baseUrl)}/api/v1/models`,
231
+ emptyHeaders,
232
+ config.timeout
233
+ )
234
+ .pipe(
235
+ Effect.match({
236
+ onFailure: () => false,
237
+ onSuccess: () => true
238
+ })
239
+ )
240
+
241
+ return makeApiConnector({
242
+ id: ConnectorIds.LmStudio,
243
+ executeStream: (prompt) =>
244
+ streamRequest([
245
+ LmStudioMessage.make({
246
+ role: "user",
247
+ content: prompt
248
+ })
249
+ ]),
250
+ executeStreamWithHistory: (messages) => streamRequest(lmStudioHistory(messages)),
251
+ executeWithTools: () =>
252
+ Effect.fail(
253
+ InvalidRequestError.make({
254
+ message: "LmStudio provider does not yet support tool calling in this implementation"
255
+ })
256
+ ),
257
+ executeStructuredWithUsage,
258
+ isAvailable
259
+ })
260
+ }