@llm4ts/core 2.54.0 → 3.0.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 (142) hide show
  1. package/README.md +1 -1
  2. package/dist/Capability.d.ts +0 -9
  3. package/dist/Capability.d.ts.map +1 -1
  4. package/dist/Capability.js +0 -38
  5. package/dist/Capability.js.map +1 -1
  6. package/dist/Connector.d.ts +2 -3
  7. package/dist/Connector.d.ts.map +1 -1
  8. package/dist/Connector.js +0 -5
  9. package/dist/Connector.js.map +1 -1
  10. package/dist/ConnectorRegistry.d.ts +0 -3
  11. package/dist/ConnectorRegistry.d.ts.map +1 -1
  12. package/dist/ConnectorRegistry.js +0 -3
  13. package/dist/ConnectorRegistry.js.map +1 -1
  14. package/dist/HttpClient.d.ts +0 -2
  15. package/dist/HttpClient.d.ts.map +1 -1
  16. package/dist/HttpClient.js +0 -2
  17. package/dist/HttpClient.js.map +1 -1
  18. package/dist/LlmService.d.ts +1 -2
  19. package/dist/LlmService.d.ts.map +1 -1
  20. package/dist/LlmService.js.map +1 -1
  21. package/dist/Models.d.ts +0 -21
  22. package/dist/Models.d.ts.map +1 -1
  23. package/dist/Models.js +0 -19
  24. package/dist/Models.js.map +1 -1
  25. package/dist/Streaming.d.ts +4 -11
  26. package/dist/Streaming.d.ts.map +1 -1
  27. package/dist/Streaming.js +2 -29
  28. package/dist/Streaming.js.map +1 -1
  29. package/dist/providers/AnthropicModels.d.ts +0 -43
  30. package/dist/providers/AnthropicModels.d.ts.map +1 -1
  31. package/dist/providers/AnthropicModels.js +0 -39
  32. package/dist/providers/AnthropicModels.js.map +1 -1
  33. package/dist/providers/AnthropicProvider.d.ts +2 -3
  34. package/dist/providers/AnthropicProvider.d.ts.map +1 -1
  35. package/dist/providers/AnthropicProvider.js +2 -49
  36. package/dist/providers/AnthropicProvider.js.map +1 -1
  37. package/dist/providers/GeminiApiProvider.d.ts +2 -3
  38. package/dist/providers/GeminiApiProvider.d.ts.map +1 -1
  39. package/dist/providers/GeminiApiProvider.js +2 -44
  40. package/dist/providers/GeminiApiProvider.js.map +1 -1
  41. package/dist/providers/GeminiModels.d.ts +0 -48
  42. package/dist/providers/GeminiModels.d.ts.map +1 -1
  43. package/dist/providers/GeminiModels.js +0 -40
  44. package/dist/providers/GeminiModels.js.map +1 -1
  45. package/dist/providers/LmStudioProvider.d.ts.map +1 -1
  46. package/dist/providers/LmStudioProvider.js +1 -4
  47. package/dist/providers/LmStudioProvider.js.map +1 -1
  48. package/dist/providers/MlxLmProvider.d.ts.map +1 -1
  49. package/dist/providers/MlxLmProvider.js +1 -2
  50. package/dist/providers/MlxLmProvider.js.map +1 -1
  51. package/dist/providers/MockProvider.d.ts.map +1 -1
  52. package/dist/providers/MockProvider.js +1 -6
  53. package/dist/providers/MockProvider.js.map +1 -1
  54. package/dist/providers/OllamaProvider.d.ts.map +1 -1
  55. package/dist/providers/OllamaProvider.js +1 -4
  56. package/dist/providers/OllamaProvider.js.map +1 -1
  57. package/dist/providers/OpenAIModels.d.ts +0 -58
  58. package/dist/providers/OpenAIModels.d.ts.map +1 -1
  59. package/dist/providers/OpenAIModels.js +0 -51
  60. package/dist/providers/OpenAIModels.js.map +1 -1
  61. package/dist/providers/OpenAIProvider.d.ts +2 -3
  62. package/dist/providers/OpenAIProvider.d.ts.map +1 -1
  63. package/dist/providers/OpenAIProvider.js +2 -46
  64. package/dist/providers/OpenAIProvider.js.map +1 -1
  65. package/package.json +2 -14
  66. package/src/Capability.ts +0 -50
  67. package/src/Connector.ts +2 -20
  68. package/src/ConnectorRegistry.ts +0 -15
  69. package/src/HttpClient.ts +0 -15
  70. package/src/LlmService.ts +1 -13
  71. package/src/Models.ts +0 -22
  72. package/src/Streaming.ts +3 -99
  73. package/src/providers/AnthropicModels.ts +0 -47
  74. package/src/providers/AnthropicProvider.ts +1 -87
  75. package/src/providers/GeminiApiProvider.ts +2 -83
  76. package/src/providers/GeminiModels.ts +0 -46
  77. package/src/providers/LmStudioProvider.ts +1 -7
  78. package/src/providers/MlxLmProvider.ts +1 -3
  79. package/src/providers/MockProvider.ts +0 -9
  80. package/src/providers/OllamaProvider.ts +1 -7
  81. package/src/providers/OpenAIModels.ts +0 -64
  82. package/src/providers/OpenAIProvider.ts +2 -83
  83. package/dist/ContextManagement.d.ts +0 -78
  84. package/dist/ContextManagement.d.ts.map +0 -1
  85. package/dist/ContextManagement.js +0 -222
  86. package/dist/ContextManagement.js.map +0 -1
  87. package/dist/Conversation.d.ts +0 -18
  88. package/dist/Conversation.d.ts.map +0 -1
  89. package/dist/Conversation.js +0 -17
  90. package/dist/Conversation.js.map +0 -1
  91. package/dist/RateLimiter.d.ts +0 -44
  92. package/dist/RateLimiter.d.ts.map +0 -1
  93. package/dist/RateLimiter.js +0 -155
  94. package/dist/RateLimiter.js.map +0 -1
  95. package/dist/observability/MeteredLlmService.d.ts +0 -16
  96. package/dist/observability/MeteredLlmService.d.ts.map +0 -1
  97. package/dist/observability/MeteredLlmService.js +0 -72
  98. package/dist/observability/MeteredLlmService.js.map +0 -1
  99. package/dist/observability/Metrics.d.ts +0 -76
  100. package/dist/observability/Metrics.d.ts.map +0 -1
  101. package/dist/observability/Metrics.js +0 -152
  102. package/dist/observability/Metrics.js.map +0 -1
  103. package/dist/observability/StructuredLogger.d.ts +0 -31
  104. package/dist/observability/StructuredLogger.d.ts.map +0 -1
  105. package/dist/observability/StructuredLogger.js +0 -39
  106. package/dist/observability/StructuredLogger.js.map +0 -1
  107. package/dist/observability/Tracing.d.ts +0 -38
  108. package/dist/observability/Tracing.d.ts.map +0 -1
  109. package/dist/observability/Tracing.js +0 -80
  110. package/dist/observability/Tracing.js.map +0 -1
  111. package/dist/providers/OpenCodeModels.d.ts +0 -61
  112. package/dist/providers/OpenCodeModels.d.ts.map +0 -1
  113. package/dist/providers/OpenCodeModels.js +0 -53
  114. package/dist/providers/OpenCodeModels.js.map +0 -1
  115. package/dist/providers/OpenCodeProvider.d.ts +0 -10
  116. package/dist/providers/OpenCodeProvider.d.ts.map +0 -1
  117. package/dist/providers/OpenCodeProvider.js +0 -154
  118. package/dist/providers/OpenCodeProvider.js.map +0 -1
  119. package/dist/tools/Tool.d.ts +0 -83
  120. package/dist/tools/Tool.d.ts.map +0 -1
  121. package/dist/tools/Tool.js +0 -278
  122. package/dist/tools/Tool.js.map +0 -1
  123. package/dist/tools/ToolCallingExecutor.d.ts +0 -18
  124. package/dist/tools/ToolCallingExecutor.d.ts.map +0 -1
  125. package/dist/tools/ToolCallingExecutor.js +0 -64
  126. package/dist/tools/ToolCallingExecutor.js.map +0 -1
  127. package/dist/tools/ToolRegistry.d.ts +0 -45
  128. package/dist/tools/ToolRegistry.d.ts.map +0 -1
  129. package/dist/tools/ToolRegistry.js +0 -133
  130. package/dist/tools/ToolRegistry.js.map +0 -1
  131. package/src/ContextManagement.ts +0 -367
  132. package/src/Conversation.ts +0 -21
  133. package/src/RateLimiter.ts +0 -238
  134. package/src/observability/MeteredLlmService.ts +0 -168
  135. package/src/observability/Metrics.ts +0 -200
  136. package/src/observability/StructuredLogger.ts +0 -67
  137. package/src/observability/Tracing.ts +0 -146
  138. package/src/providers/OpenCodeModels.ts +0 -60
  139. package/src/providers/OpenCodeProvider.ts +0 -278
  140. package/src/tools/Tool.ts +0 -376
  141. package/src/tools/ToolCallingExecutor.ts +0 -106
  142. package/src/tools/ToolRegistry.ts +0 -224
@@ -1,146 +0,0 @@
1
- /**
2
- * @deprecated Since 2.30.0 (ADR 0026): llm4ts exports OpenTelemetry spans and
3
- * metrics over OTLP from `@llm4ts/runner/Otel`; this in-memory module is not
4
- * used by flow or runner and will be removed in the next major.
5
- */
6
- import * as Clock from "effect/Clock"
7
- import * as Context from "effect/Context"
8
- import * as Effect from "effect/Effect"
9
- import * as Exit from "effect/Exit"
10
- import * as Layer from "effect/Layer"
11
- import * as Ref from "effect/Ref"
12
- import * as Schema from "effect/Schema"
13
- import type { JsonValue } from "../providers/CliSupport.ts"
14
- import { redactText, sanitizeFields, type RedactionOptions } from "./Redaction.ts"
15
-
16
- export const TraceStatus = Schema.Literals(["Ok", "Error"])
17
- export type TraceStatus = typeof TraceStatus.Type
18
-
19
- export class TraceSpan extends Schema.Class<TraceSpan>("TraceSpan")({
20
- traceId: Schema.String,
21
- spanId: Schema.String,
22
- parentSpanId: Schema.optionalKey(Schema.String),
23
- correlationId: Schema.String,
24
- name: Schema.String,
25
- status: TraceStatus,
26
- startedAt: Schema.Number,
27
- endedAt: Schema.Number,
28
- attributes: Schema.Record(Schema.String, Schema.Json),
29
- errorMessage: Schema.optionalKey(Schema.String)
30
- }) {}
31
-
32
- interface TraceContext {
33
- readonly traceId: string
34
- readonly spanId: string
35
- readonly correlationId: string
36
- }
37
-
38
- const CurrentTraceContext = Context.Reference<TraceContext | undefined>(
39
- "@llm4ts/core/observability/CurrentTraceContext",
40
- { defaultValue: () => undefined }
41
- )
42
-
43
- export interface TracingServiceShape {
44
- readonly withCorrelationId: <A, E, R>(
45
- id: string,
46
- effect: Effect.Effect<A, E, R>
47
- ) => Effect.Effect<A, E, R>
48
- readonly correlationId: Effect.Effect<string>
49
- readonly inSpan: <A, E, R>(
50
- name: string,
51
- attributes: Readonly<Record<string, JsonValue>>,
52
- effect: Effect.Effect<A, E, R>
53
- ) => Effect.Effect<A, E, R>
54
- readonly recordedSpans: Effect.Effect<ReadonlyArray<TraceSpan>>
55
- }
56
-
57
- export class TracingService extends Context.Service<TracingService, TracingServiceShape>()(
58
- "@llm4ts/core/observability/TracingService"
59
- ) {}
60
-
61
- export interface TracingOptions extends RedactionOptions {
62
- readonly makeId?: Effect.Effect<string>
63
- }
64
-
65
- const defaultId = Effect.sync(() => globalThis.crypto.randomUUID())
66
-
67
- export const makeTracingService = Effect.fn("@llm4ts/core/observability/TracingService.make")(
68
- function* (options: TracingOptions = {}): Effect.fn.Return<TracingServiceShape> {
69
- const spans = yield* Ref.make<ReadonlyArray<TraceSpan>>([])
70
- const makeId = options.makeId ?? defaultId
71
-
72
- const correlationId = Effect.flatMap(CurrentTraceContext, (current) =>
73
- current === undefined ? makeId : Effect.succeed(current.correlationId)
74
- )
75
-
76
- const withCorrelationId = <A, E, R>(
77
- id: string,
78
- effect: Effect.Effect<A, E, R>
79
- ): Effect.Effect<A, E, R> =>
80
- Effect.flatMap(CurrentTraceContext, (current) =>
81
- Effect.provideService(effect, CurrentTraceContext, {
82
- traceId: current?.traceId ?? id,
83
- spanId: current?.spanId ?? id,
84
- correlationId: id
85
- })
86
- )
87
-
88
- const inSpan = <A, E, R>(
89
- name: string,
90
- attributes: Readonly<Record<string, JsonValue>>,
91
- effect: Effect.Effect<A, E, R>
92
- ): Effect.Effect<A, E, R> =>
93
- Effect.gen(function* () {
94
- const parent = yield* CurrentTraceContext
95
- const correlation = yield* correlationId
96
- const traceId = parent?.traceId ?? (yield* makeId)
97
- const spanId = yield* makeId
98
- const startedAt = yield* Clock.currentTimeMillis
99
- const next: TraceContext = {
100
- traceId,
101
- spanId,
102
- correlationId: correlation
103
- }
104
- return yield* Effect.provideService(
105
- effect.pipe(
106
- Effect.onExit((exit) =>
107
- Clock.currentTimeMillis.pipe(
108
- Effect.flatMap((endedAt) =>
109
- Ref.update(spans, (current) => [
110
- ...current,
111
- TraceSpan.make({
112
- traceId,
113
- spanId,
114
- ...(parent === undefined ? {} : { parentSpanId: parent.spanId }),
115
- correlationId: correlation,
116
- name,
117
- status: Exit.isSuccess(exit) ? "Ok" : "Error",
118
- startedAt,
119
- endedAt,
120
- attributes: sanitizeFields(attributes, options),
121
- ...(Exit.isFailure(exit)
122
- ? {
123
- errorMessage: redactText(String(exit.cause), options)
124
- }
125
- : {})
126
- })
127
- ])
128
- )
129
- )
130
- )
131
- ),
132
- CurrentTraceContext,
133
- next
134
- )
135
- })
136
-
137
- return {
138
- withCorrelationId,
139
- correlationId,
140
- inSpan,
141
- recordedSpans: Ref.get(spans)
142
- }
143
- }
144
- )
145
-
146
- export const TracingServiceLive = Layer.effect(TracingService, makeTracingService())
@@ -1,60 +0,0 @@
1
- import * as Schema from "effect/Schema"
2
- import { JsonSchema } from "../Models.ts"
3
-
4
- export class OpenCodeMessage extends Schema.Class<OpenCodeMessage>("OpenCodeMessage")({
5
- role: Schema.String,
6
- content: Schema.String
7
- }) {}
8
-
9
- export class OpenCodeJsonSchemaSpec extends Schema.Class<OpenCodeJsonSchemaSpec>(
10
- "OpenCodeJsonSchemaSpec"
11
- )({
12
- name: Schema.String,
13
- schema: JsonSchema
14
- }) {}
15
-
16
- export class OpenCodeResponseFormat extends Schema.Class<OpenCodeResponseFormat>(
17
- "OpenCodeResponseFormat"
18
- )({
19
- type: Schema.String,
20
- json_schema: Schema.optionalKey(OpenCodeJsonSchemaSpec)
21
- }) {}
22
-
23
- export class OpenCodeCompletionRequest extends Schema.Class<OpenCodeCompletionRequest>(
24
- "OpenCodeCompletionRequest"
25
- )({
26
- model: Schema.String,
27
- messages: Schema.Array(OpenCodeMessage),
28
- temperature: Schema.optionalKey(Schema.Number),
29
- max_tokens: Schema.optionalKey(Schema.Int),
30
- max_completion_tokens: Schema.optionalKey(Schema.Int),
31
- stream: Schema.optionalKey(Schema.Boolean),
32
- response_format: Schema.optionalKey(OpenCodeResponseFormat)
33
- }) {}
34
-
35
- export class OpenCodeDelta extends Schema.Class<OpenCodeDelta>("OpenCodeDelta")({
36
- content: Schema.optionalKey(Schema.NullOr(Schema.String))
37
- }) {}
38
-
39
- export class OpenCodeChoice extends Schema.Class<OpenCodeChoice>("OpenCodeChoice")({
40
- index: Schema.optionalKey(Schema.Int),
41
- message: Schema.optionalKey(OpenCodeMessage),
42
- delta: Schema.optionalKey(OpenCodeDelta),
43
- text: Schema.optionalKey(Schema.NullOr(Schema.String)),
44
- finish_reason: Schema.optionalKey(Schema.NullOr(Schema.String))
45
- }) {}
46
-
47
- export class OpenCodeTokenUsage extends Schema.Class<OpenCodeTokenUsage>("OpenCodeTokenUsage")({
48
- prompt_tokens: Schema.optionalKey(Schema.Int),
49
- completion_tokens: Schema.optionalKey(Schema.Int),
50
- total_tokens: Schema.optionalKey(Schema.Int)
51
- }) {}
52
-
53
- export class OpenCodeCompletionResponse extends Schema.Class<OpenCodeCompletionResponse>(
54
- "OpenCodeCompletionResponse"
55
- )({
56
- id: Schema.optionalKey(Schema.String),
57
- choices: Schema.Array(OpenCodeChoice),
58
- usage: Schema.optionalKey(OpenCodeTokenUsage),
59
- model: Schema.optionalKey(Schema.String)
60
- }) {}
@@ -1,278 +0,0 @@
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 {
7
- AuthenticationError,
8
- ConfigError,
9
- InvalidRequestError,
10
- ParseError,
11
- type LlmError
12
- } from "../Errors.ts"
13
- import type { HttpClientShape } from "../HttpClient.ts"
14
- import type { StructuredResult } from "../LlmService.ts"
15
- import {
16
- ConnectorIds,
17
- LlmChunk,
18
- TokenUsage,
19
- type JsonSchema,
20
- type LlmConfig,
21
- type Message,
22
- type MessageRole
23
- } from "../Models.ts"
24
- import { parseFromText } from "../StructuredOutput.ts"
25
- import {
26
- OpenCodeCompletionRequest,
27
- OpenCodeCompletionResponse,
28
- OpenCodeJsonSchemaSpec,
29
- OpenCodeMessage,
30
- OpenCodeResponseFormat
31
- } from "./OpenCodeModels.ts"
32
-
33
- interface OpenCodeRequiredConfig {
34
- readonly baseUrl: string
35
- readonly apiKey: string
36
- }
37
-
38
- const roleName = (role: MessageRole): string => {
39
- switch (role) {
40
- case "System":
41
- return "system"
42
- case "User":
43
- return "user"
44
- case "Assistant":
45
- return "assistant"
46
- case "Tool":
47
- return "tool"
48
- }
49
- }
50
-
51
- export const openCodeHistory = (messages: ReadonlyArray<Message>): ReadonlyArray<OpenCodeMessage> =>
52
- messages.map((message) =>
53
- OpenCodeMessage.make({
54
- role: roleName(message.role),
55
- content: message.content
56
- })
57
- )
58
-
59
- const usage = (response: OpenCodeCompletionResponse): TokenUsage | undefined =>
60
- response.usage === undefined
61
- ? undefined
62
- : TokenUsage.make({
63
- prompt: response.usage.prompt_tokens ?? 0,
64
- completion: response.usage.completion_tokens ?? 0,
65
- total: response.usage.total_tokens ?? 0
66
- })
67
-
68
- const metadata = (
69
- config: LlmConfig,
70
- response: OpenCodeCompletionResponse
71
- ): Readonly<Record<string, string>> => ({
72
- provider: "opencode",
73
- model: config.model,
74
- ...(response.id === undefined ? {} : { id: response.id }),
75
- ...(response.model === undefined ? {} : { response_model: response.model })
76
- })
77
-
78
- const decodeResponse = (
79
- raw: string,
80
- context: string
81
- ): Effect.Effect<OpenCodeCompletionResponse, ParseError> =>
82
- Schema.decodeUnknownEffect(Schema.fromJsonString(OpenCodeCompletionResponse))(raw).pipe(
83
- Effect.mapError((error) =>
84
- ParseError.make({
85
- message: `${context}: ${String(error)}`,
86
- raw
87
- })
88
- )
89
- )
90
-
91
- const toChunk = (config: LlmConfig, response: OpenCodeCompletionResponse): LlmChunk => {
92
- const choice = response.choices[0]
93
- const delta = choice?.delta?.content ?? choice?.message?.content ?? choice?.text ?? ""
94
- const finishReason = choice?.finish_reason ?? undefined
95
- const tokenUsage = usage(response)
96
- return LlmChunk.make({
97
- delta,
98
- metadata: metadata(config, response),
99
- ...(finishReason === undefined || finishReason === null ? {} : { finishReason }),
100
- ...(tokenUsage === undefined ? {} : { usage: tokenUsage })
101
- })
102
- }
103
-
104
- export const parseOpenCodeSseBody = Effect.fn("@llm4ts/core/OpenCodeProvider.parseSseBody")(
105
- function* (config: LlmConfig, raw: string): Effect.fn.Return<ReadonlyArray<LlmChunk>, LlmError> {
106
- const payloads = raw
107
- .split(/\r?\n/)
108
- .map((line) => line.trim())
109
- .filter((line) => line.startsWith("data:"))
110
- .map((line) => line.slice("data:".length).trim())
111
- .filter((payload) => payload.length > 0 && payload !== "[DONE]")
112
- const chunks = yield* Effect.forEach(payloads, (payload) =>
113
- Effect.map(decodeResponse(payload, "Failed to parse OpenCode SSE payload"), (response) =>
114
- toChunk(config, response)
115
- )
116
- )
117
- return chunks.length === 0
118
- ? yield* Effect.fail(
119
- ParseError.make({
120
- message: "No stream chunks parsed from OpenCode SSE response",
121
- raw
122
- })
123
- )
124
- : chunks
125
- }
126
- )
127
-
128
- export const makeOpenCodeProvider = (
129
- config: LlmConfig,
130
- httpClient: HttpClientShape
131
- ): ApiConnectorShape => {
132
- const requiredConfig: Effect.Effect<OpenCodeRequiredConfig, LlmError> =
133
- config.baseUrl === undefined
134
- ? Effect.fail(
135
- ConfigError.make({
136
- message: "Missing baseUrl for OpenCode provider"
137
- })
138
- )
139
- : config.apiKey === undefined
140
- ? Effect.fail(
141
- AuthenticationError.make({
142
- message: "Missing API key for OpenCode provider (pass apiKey in the connector config)"
143
- })
144
- )
145
- : Effect.succeed({
146
- baseUrl: config.baseUrl.replace(/\/+$/, ""),
147
- apiKey: Redacted.value(config.apiKey)
148
- })
149
-
150
- const request = (
151
- messages: ReadonlyArray<OpenCodeMessage>,
152
- stream: boolean,
153
- responseFormat?: OpenCodeResponseFormat
154
- ): OpenCodeCompletionRequest =>
155
- OpenCodeCompletionRequest.make({
156
- model: config.model,
157
- messages,
158
- temperature: config.temperature ?? 0.7,
159
- stream,
160
- ...(config.maxTokens === undefined ? {} : { max_tokens: config.maxTokens }),
161
- ...(responseFormat === undefined ? {} : { response_format: responseFormat })
162
- })
163
-
164
- const post = (
165
- messages: ReadonlyArray<OpenCodeMessage>,
166
- stream: boolean,
167
- responseFormat?: OpenCodeResponseFormat
168
- ): Effect.Effect<string, LlmError> =>
169
- Effect.flatMap(requiredConfig, ({ apiKey, baseUrl }) =>
170
- httpClient.postJson(
171
- `${baseUrl}/chat/completions`,
172
- JSON.stringify(request(messages, stream, responseFormat)),
173
- {
174
- Authorization: `Bearer ${apiKey}`
175
- },
176
- config.timeout
177
- )
178
- )
179
-
180
- const executeResponse = (
181
- messages: ReadonlyArray<OpenCodeMessage>,
182
- responseFormat?: OpenCodeResponseFormat
183
- ): Effect.Effect<readonly [response: OpenCodeCompletionResponse, raw: string], LlmError> =>
184
- Effect.gen(function* () {
185
- const raw = yield* post(messages, false, responseFormat)
186
- const response = yield* decodeResponse(raw, "Failed to decode OpenCode response")
187
- return [response, raw]
188
- })
189
-
190
- const responseContent = (
191
- response: OpenCodeCompletionResponse,
192
- raw: string
193
- ): Effect.Effect<string, ParseError> => {
194
- const content = (response.choices[0]?.message?.content ?? response.choices[0]?.text)?.trim()
195
- return content === undefined || content === null || content.length === 0
196
- ? Effect.fail(
197
- ParseError.make({
198
- message: "OpenCode response missing choices[0].message.content",
199
- raw
200
- })
201
- )
202
- : Effect.succeed(content)
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 responseFormat = OpenCodeResponseFormat.make({
212
- type: "json_schema",
213
- json_schema: OpenCodeJsonSchemaSpec.make({
214
- name: "response",
215
- schema: jsonSchema
216
- })
217
- })
218
- const [response, raw] = yield* executeResponse(
219
- [
220
- OpenCodeMessage.make({
221
- role: "user",
222
- content: prompt
223
- })
224
- ],
225
- responseFormat
226
- )
227
- const content = yield* responseContent(response, raw)
228
- const value = yield* parseFromText(content, schema, jsonSchema)
229
- const result: StructuredResult<A> = [value, undefined, undefined]
230
- return result
231
- })
232
-
233
- const stream = (messages: ReadonlyArray<OpenCodeMessage>): Stream.Stream<LlmChunk, LlmError> =>
234
- Stream.flatMap(
235
- Stream.fromEffect(
236
- Effect.flatMap(post(messages, true), (raw) => parseOpenCodeSseBody(config, raw))
237
- ),
238
- Stream.fromIterable
239
- )
240
-
241
- const isAvailable: Effect.Effect<boolean> =
242
- config.baseUrl === undefined || config.apiKey === undefined
243
- ? Effect.succeed(false)
244
- : httpClient
245
- .get(
246
- `${config.baseUrl.replace(/\/+$/, "")}/models`,
247
- {
248
- Authorization: `Bearer ${Redacted.value(config.apiKey)}`
249
- },
250
- config.timeout
251
- )
252
- .pipe(
253
- Effect.match({
254
- onFailure: () => false,
255
- onSuccess: () => true
256
- })
257
- )
258
-
259
- return makeApiConnector({
260
- id: ConnectorIds.OpenCode,
261
- executeStream: (prompt) =>
262
- stream([
263
- OpenCodeMessage.make({
264
- role: "user",
265
- content: prompt
266
- })
267
- ]),
268
- executeStreamWithHistory: (messages) => stream(openCodeHistory(messages)),
269
- executeWithTools: () =>
270
- Effect.fail(
271
- InvalidRequestError.make({
272
- message: "OpenCode provider does not yet support tool calling in this implementation"
273
- })
274
- ),
275
- executeStructuredWithUsage,
276
- isAvailable
277
- })
278
- }