@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.
- package/LICENSE +21 -0
- package/README.md +47 -0
- package/dist/AgentSession.d.ts +66 -0
- package/dist/AgentSession.d.ts.map +1 -0
- package/dist/AgentSession.js +53 -0
- package/dist/AgentSession.js.map +1 -0
- package/dist/Capability.d.ts +101 -0
- package/dist/Capability.d.ts.map +1 -0
- package/dist/Capability.js +210 -0
- package/dist/Capability.js.map +1 -0
- package/dist/Connector.d.ts +69 -0
- package/dist/Connector.d.ts.map +1 -0
- package/dist/Connector.js +86 -0
- package/dist/Connector.js.map +1 -0
- package/dist/ConnectorConfig.d.ts +70 -0
- package/dist/ConnectorConfig.d.ts.map +1 -0
- package/dist/ConnectorConfig.js +110 -0
- package/dist/ConnectorConfig.js.map +1 -0
- package/dist/ConnectorRegistry.d.ts +28 -0
- package/dist/ConnectorRegistry.d.ts.map +1 -0
- package/dist/ConnectorRegistry.js +87 -0
- package/dist/ConnectorRegistry.js.map +1 -0
- package/dist/ContextManagement.d.ts +78 -0
- package/dist/ContextManagement.d.ts.map +1 -0
- package/dist/ContextManagement.js +221 -0
- package/dist/ContextManagement.js.map +1 -0
- package/dist/Conversation.d.ts +18 -0
- package/dist/Conversation.d.ts.map +1 -0
- package/dist/Conversation.js +17 -0
- package/dist/Conversation.js.map +1 -0
- package/dist/Errors.d.ts +64 -0
- package/dist/Errors.d.ts.map +1 -0
- package/dist/Errors.js +76 -0
- package/dist/Errors.js.map +1 -0
- package/dist/HttpClient.d.ts +43 -0
- package/dist/HttpClient.d.ts.map +1 -0
- package/dist/HttpClient.js +99 -0
- package/dist/HttpClient.js.map +1 -0
- package/dist/LlmService.d.ts +24 -0
- package/dist/LlmService.d.ts.map +1 -0
- package/dist/LlmService.js +4 -0
- package/dist/LlmService.js.map +1 -0
- package/dist/Models.d.ts +138 -0
- package/dist/Models.d.ts.map +1 -0
- package/dist/Models.js +209 -0
- package/dist/Models.js.map +1 -0
- package/dist/ProcessExecutor.d.ts +57 -0
- package/dist/ProcessExecutor.d.ts.map +1 -0
- package/dist/ProcessExecutor.js +71 -0
- package/dist/ProcessExecutor.js.map +1 -0
- package/dist/RateLimiter.d.ts +44 -0
- package/dist/RateLimiter.d.ts.map +1 -0
- package/dist/RateLimiter.js +155 -0
- package/dist/RateLimiter.js.map +1 -0
- package/dist/Streaming.d.ts +17 -0
- package/dist/Streaming.d.ts.map +1 -0
- package/dist/Streaming.js +65 -0
- package/dist/Streaming.js.map +1 -0
- package/dist/StructuredOutput.d.ts +10 -0
- package/dist/StructuredOutput.d.ts.map +1 -0
- package/dist/StructuredOutput.js +97 -0
- package/dist/StructuredOutput.js.map +1 -0
- package/dist/TemporaryFiles.d.ts +20 -0
- package/dist/TemporaryFiles.d.ts.map +1 -0
- package/dist/TemporaryFiles.js +18 -0
- package/dist/TemporaryFiles.js.map +1 -0
- package/dist/UsageLimits.d.ts +5 -0
- package/dist/UsageLimits.d.ts.map +1 -0
- package/dist/UsageLimits.js +187 -0
- package/dist/UsageLimits.js.map +1 -0
- package/dist/eval/Checks.d.ts +6 -0
- package/dist/eval/Checks.d.ts.map +1 -0
- package/dist/eval/Checks.js +39 -0
- package/dist/eval/Checks.js.map +1 -0
- package/dist/eval/Eval.d.ts +57 -0
- package/dist/eval/Eval.d.ts.map +1 -0
- package/dist/eval/Eval.js +91 -0
- package/dist/eval/Eval.js.map +1 -0
- package/dist/eval/EvalSuite.d.ts +21 -0
- package/dist/eval/EvalSuite.d.ts.map +1 -0
- package/dist/eval/EvalSuite.js +30 -0
- package/dist/eval/EvalSuite.js.map +1 -0
- package/dist/eval/Evaluator.d.ts +10 -0
- package/dist/eval/Evaluator.d.ts.map +1 -0
- package/dist/eval/Evaluator.js +14 -0
- package/dist/eval/Evaluator.js.map +1 -0
- package/dist/eval/Judge.d.ts +15 -0
- package/dist/eval/Judge.d.ts.map +1 -0
- package/dist/eval/Judge.js +73 -0
- package/dist/eval/Judge.js.map +1 -0
- package/dist/observability/MeteredLlmService.d.ts +16 -0
- package/dist/observability/MeteredLlmService.d.ts.map +1 -0
- package/dist/observability/MeteredLlmService.js +66 -0
- package/dist/observability/MeteredLlmService.js.map +1 -0
- package/dist/observability/Metrics.d.ts +71 -0
- package/dist/observability/Metrics.d.ts.map +1 -0
- package/dist/observability/Metrics.js +147 -0
- package/dist/observability/Metrics.js.map +1 -0
- package/dist/observability/Redaction.d.ts +8 -0
- package/dist/observability/Redaction.d.ts.map +1 -0
- package/dist/observability/Redaction.js +38 -0
- package/dist/observability/Redaction.js.map +1 -0
- package/dist/observability/StreamRecorder.d.ts +9 -0
- package/dist/observability/StreamRecorder.d.ts.map +1 -0
- package/dist/observability/StreamRecorder.js +10 -0
- package/dist/observability/StreamRecorder.js.map +1 -0
- package/dist/observability/StructuredLogger.d.ts +31 -0
- package/dist/observability/StructuredLogger.d.ts.map +1 -0
- package/dist/observability/StructuredLogger.js +34 -0
- package/dist/observability/StructuredLogger.js.map +1 -0
- package/dist/observability/Tracing.d.ts +38 -0
- package/dist/observability/Tracing.d.ts.map +1 -0
- package/dist/observability/Tracing.js +75 -0
- package/dist/observability/Tracing.js.map +1 -0
- package/dist/providers/AnthropicModels.d.ts +95 -0
- package/dist/providers/AnthropicModels.d.ts.map +1 -0
- package/dist/providers/AnthropicModels.js +83 -0
- package/dist/providers/AnthropicModels.js.map +1 -0
- package/dist/providers/AnthropicProvider.d.ts +13 -0
- package/dist/providers/AnthropicProvider.d.ts.map +1 -0
- package/dist/providers/AnthropicProvider.js +177 -0
- package/dist/providers/AnthropicProvider.js.map +1 -0
- package/dist/providers/AntigravityConnector.d.ts +7 -0
- package/dist/providers/AntigravityConnector.d.ts.map +1 -0
- package/dist/providers/AntigravityConnector.js +52 -0
- package/dist/providers/AntigravityConnector.js.map +1 -0
- package/dist/providers/ClaudeAgentSession.d.ts +11 -0
- package/dist/providers/ClaudeAgentSession.d.ts.map +1 -0
- package/dist/providers/ClaudeAgentSession.js +158 -0
- package/dist/providers/ClaudeAgentSession.js.map +1 -0
- package/dist/providers/ClaudeCliConnector.d.ts +9 -0
- package/dist/providers/ClaudeCliConnector.d.ts.map +1 -0
- package/dist/providers/ClaudeCliConnector.js +123 -0
- package/dist/providers/ClaudeCliConnector.js.map +1 -0
- package/dist/providers/CliSupport.d.ts +21 -0
- package/dist/providers/CliSupport.d.ts.map +1 -0
- package/dist/providers/CliSupport.js +62 -0
- package/dist/providers/CliSupport.js.map +1 -0
- package/dist/providers/CodexConnector.d.ts +12 -0
- package/dist/providers/CodexConnector.d.ts.map +1 -0
- package/dist/providers/CodexConnector.js +175 -0
- package/dist/providers/CodexConnector.js.map +1 -0
- package/dist/providers/ConnectorFactories.d.ts +15 -0
- package/dist/providers/ConnectorFactories.d.ts.map +1 -0
- package/dist/providers/ConnectorFactories.js +74 -0
- package/dist/providers/ConnectorFactories.js.map +1 -0
- package/dist/providers/CopilotConnector.d.ts +6 -0
- package/dist/providers/CopilotConnector.d.ts.map +1 -0
- package/dist/providers/CopilotConnector.js +35 -0
- package/dist/providers/CopilotConnector.js.map +1 -0
- package/dist/providers/CursorConnector.d.ts +8 -0
- package/dist/providers/CursorConnector.d.ts.map +1 -0
- package/dist/providers/CursorConnector.js +82 -0
- package/dist/providers/CursorConnector.js.map +1 -0
- package/dist/providers/GeminiApiProvider.d.ts +9 -0
- package/dist/providers/GeminiApiProvider.d.ts.map +1 -0
- package/dist/providers/GeminiApiProvider.js +181 -0
- package/dist/providers/GeminiApiProvider.js.map +1 -0
- package/dist/providers/GeminiCliProvider.d.ts +106 -0
- package/dist/providers/GeminiCliProvider.d.ts.map +1 -0
- package/dist/providers/GeminiCliProvider.js +596 -0
- package/dist/providers/GeminiCliProvider.js.map +1 -0
- package/dist/providers/GeminiModels.d.ts +92 -0
- package/dist/providers/GeminiModels.d.ts.map +1 -0
- package/dist/providers/GeminiModels.js +80 -0
- package/dist/providers/GeminiModels.js.map +1 -0
- package/dist/providers/GrokCliConnector.d.ts +8 -0
- package/dist/providers/GrokCliConnector.d.ts.map +1 -0
- package/dist/providers/GrokCliConnector.js +82 -0
- package/dist/providers/GrokCliConnector.js.map +1 -0
- package/dist/providers/LmStudioModels.d.ts +114 -0
- package/dist/providers/LmStudioModels.d.ts.map +1 -0
- package/dist/providers/LmStudioModels.js +102 -0
- package/dist/providers/LmStudioModels.js.map +1 -0
- package/dist/providers/LmStudioProvider.d.ts +13 -0
- package/dist/providers/LmStudioProvider.d.ts.map +1 -0
- package/dist/providers/LmStudioProvider.js +158 -0
- package/dist/providers/LmStudioProvider.js.map +1 -0
- package/dist/providers/MockProvider.d.ts +6 -0
- package/dist/providers/MockProvider.d.ts.map +1 -0
- package/dist/providers/MockProvider.js +271 -0
- package/dist/providers/MockProvider.js.map +1 -0
- package/dist/providers/OllamaModels.d.ts +68 -0
- package/dist/providers/OllamaModels.d.ts.map +1 -0
- package/dist/providers/OllamaModels.js +59 -0
- package/dist/providers/OllamaModels.js.map +1 -0
- package/dist/providers/OllamaProvider.d.ts +7 -0
- package/dist/providers/OllamaProvider.d.ts.map +1 -0
- package/dist/providers/OllamaProvider.js +134 -0
- package/dist/providers/OllamaProvider.js.map +1 -0
- package/dist/providers/OpenAIModels.d.ts +139 -0
- package/dist/providers/OpenAIModels.d.ts.map +1 -0
- package/dist/providers/OpenAIModels.js +123 -0
- package/dist/providers/OpenAIModels.js.map +1 -0
- package/dist/providers/OpenAIProvider.d.ts +9 -0
- package/dist/providers/OpenAIProvider.d.ts.map +1 -0
- package/dist/providers/OpenAIProvider.js +185 -0
- package/dist/providers/OpenAIProvider.js.map +1 -0
- package/dist/providers/OpenCodeCliConnector.d.ts +8 -0
- package/dist/providers/OpenCodeCliConnector.d.ts.map +1 -0
- package/dist/providers/OpenCodeCliConnector.js +93 -0
- package/dist/providers/OpenCodeCliConnector.js.map +1 -0
- package/dist/providers/OpenCodeModels.d.ts +61 -0
- package/dist/providers/OpenCodeModels.d.ts.map +1 -0
- package/dist/providers/OpenCodeModels.js +53 -0
- package/dist/providers/OpenCodeModels.js.map +1 -0
- package/dist/providers/OpenCodeProvider.d.ts +10 -0
- package/dist/providers/OpenCodeProvider.d.ts.map +1 -0
- package/dist/providers/OpenCodeProvider.js +154 -0
- package/dist/providers/OpenCodeProvider.js.map +1 -0
- package/dist/providers/PiConnector.d.ts +8 -0
- package/dist/providers/PiConnector.d.ts.map +1 -0
- package/dist/providers/PiConnector.js +99 -0
- package/dist/providers/PiConnector.js.map +1 -0
- package/dist/tools/Tool.d.ts +83 -0
- package/dist/tools/Tool.d.ts.map +1 -0
- package/dist/tools/Tool.js +278 -0
- package/dist/tools/Tool.js.map +1 -0
- package/dist/tools/ToolCallingExecutor.d.ts +18 -0
- package/dist/tools/ToolCallingExecutor.d.ts.map +1 -0
- package/dist/tools/ToolCallingExecutor.js +64 -0
- package/dist/tools/ToolCallingExecutor.js.map +1 -0
- package/dist/tools/ToolRegistry.d.ts +45 -0
- package/dist/tools/ToolRegistry.d.ts.map +1 -0
- package/dist/tools/ToolRegistry.js +133 -0
- package/dist/tools/ToolRegistry.js.map +1 -0
- package/package.json +105 -0
- package/src/.gitkeep +1 -0
- package/src/AgentSession.ts +69 -0
- package/src/Capability.ts +240 -0
- package/src/Connector.ts +220 -0
- package/src/ConnectorConfig.ts +146 -0
- package/src/ConnectorRegistry.ts +184 -0
- package/src/ContextManagement.ts +372 -0
- package/src/Conversation.ts +21 -0
- package/src/Errors.ts +87 -0
- package/src/HttpClient.ts +202 -0
- package/src/LlmService.ts +45 -0
- package/src/Models.ts +250 -0
- package/src/ProcessExecutor.ts +157 -0
- package/src/RateLimiter.ts +238 -0
- package/src/Streaming.ts +164 -0
- package/src/StructuredOutput.ts +138 -0
- package/src/TemporaryFiles.ts +51 -0
- package/src/UsageLimits.ts +242 -0
- package/src/eval/Checks.ts +68 -0
- package/src/eval/Eval.ts +112 -0
- package/src/eval/EvalSuite.ts +63 -0
- package/src/eval/Evaluator.ts +31 -0
- package/src/eval/Judge.ts +92 -0
- package/src/observability/MeteredLlmService.ts +156 -0
- package/src/observability/Metrics.ts +195 -0
- package/src/observability/Redaction.ts +55 -0
- package/src/observability/StreamRecorder.ts +32 -0
- package/src/observability/StructuredLogger.ts +62 -0
- package/src/observability/Tracing.ts +141 -0
- package/src/providers/AnthropicModels.ts +96 -0
- package/src/providers/AnthropicProvider.ts +320 -0
- package/src/providers/AntigravityConnector.ts +77 -0
- package/src/providers/ClaudeAgentSession.ts +226 -0
- package/src/providers/ClaudeCliConnector.ts +201 -0
- package/src/providers/CliSupport.ts +100 -0
- package/src/providers/CodexConnector.ts +251 -0
- package/src/providers/ConnectorFactories.ts +142 -0
- package/src/providers/CopilotConnector.ts +47 -0
- package/src/providers/CursorConnector.ts +120 -0
- package/src/providers/GeminiApiProvider.ts +337 -0
- package/src/providers/GeminiCliProvider.ts +793 -0
- package/src/providers/GeminiModels.ts +97 -0
- package/src/providers/GrokCliConnector.ts +117 -0
- package/src/providers/LmStudioModels.ts +117 -0
- package/src/providers/LmStudioProvider.ts +260 -0
- package/src/providers/MockProvider.ts +322 -0
- package/src/providers/OllamaModels.ts +64 -0
- package/src/providers/OllamaProvider.ts +236 -0
- package/src/providers/OpenAIModels.ts +152 -0
- package/src/providers/OpenAIProvider.ts +331 -0
- package/src/providers/OpenCodeCliConnector.ts +135 -0
- package/src/providers/OpenCodeModels.ts +60 -0
- package/src/providers/OpenCodeProvider.ts +278 -0
- package/src/providers/PiConnector.ts +142 -0
- package/src/tools/Tool.ts +379 -0
- package/src/tools/ToolCallingExecutor.ts +106 -0
- package/src/tools/ToolRegistry.ts +224 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import * as Duration from "effect/Duration"
|
|
2
|
+
import * as Effect from "effect/Effect"
|
|
3
|
+
import * as Schedule from "effect/Schedule"
|
|
4
|
+
import type * as Schema from "effect/Schema"
|
|
5
|
+
import * as Stream from "effect/Stream"
|
|
6
|
+
import { apiConnectorCapabilities, type ApiConnectorShape } from "../Connector.ts"
|
|
7
|
+
import type { LlmError } from "../Errors.ts"
|
|
8
|
+
import type { StructuredResult } from "../LlmService.ts"
|
|
9
|
+
import {
|
|
10
|
+
ConnectorIds,
|
|
11
|
+
HealthStatus,
|
|
12
|
+
LlmChunk,
|
|
13
|
+
TokenUsage,
|
|
14
|
+
ToolCallResponse,
|
|
15
|
+
type JsonSchema,
|
|
16
|
+
type LlmConfig,
|
|
17
|
+
type Message
|
|
18
|
+
} from "../Models.ts"
|
|
19
|
+
import { parseFromText } from "../StructuredOutput.ts"
|
|
20
|
+
|
|
21
|
+
export const mockResponse = (): string =>
|
|
22
|
+
"This is a mock response from the demo AI provider. " +
|
|
23
|
+
"The mock provider simulates LLM output without making any real API calls. " +
|
|
24
|
+
"All features work normally except AI-generated content is replaced with pre-canned responses."
|
|
25
|
+
|
|
26
|
+
interface MockIssueTemplate {
|
|
27
|
+
readonly title: string
|
|
28
|
+
readonly tags: ReadonlyArray<string>
|
|
29
|
+
readonly capabilities: ReadonlyArray<string>
|
|
30
|
+
readonly criteria: ReadonlyArray<string>
|
|
31
|
+
readonly body: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const issueTemplates: ReadonlyArray<MockIssueTemplate> = [
|
|
35
|
+
{
|
|
36
|
+
title: "Set up Spring Boot project scaffold",
|
|
37
|
+
tags: ["setup", "bootstrap"],
|
|
38
|
+
capabilities: ["java", "spring-boot"],
|
|
39
|
+
criteria: [
|
|
40
|
+
"Maven/Gradle build compiles",
|
|
41
|
+
"Application starts on port 8080",
|
|
42
|
+
"Health endpoint returns 200"
|
|
43
|
+
],
|
|
44
|
+
body:
|
|
45
|
+
"Initialize the Spring Boot 3.x project with Java 21, configure build tool, " +
|
|
46
|
+
"and add Actuator health endpoint."
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
title: "Implement REST controller for orders",
|
|
50
|
+
tags: ["rest", "api"],
|
|
51
|
+
capabilities: ["java", "spring-boot", "rest"],
|
|
52
|
+
criteria: [
|
|
53
|
+
"GET /api/orders returns list",
|
|
54
|
+
"POST /api/orders creates order",
|
|
55
|
+
"Validation errors return 400"
|
|
56
|
+
],
|
|
57
|
+
body: "Create OrderController with CRUD endpoints, request validation, and proper HTTP status codes."
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
title: "Add JPA entity and repository for Order",
|
|
61
|
+
tags: ["database", "jpa"],
|
|
62
|
+
capabilities: ["java", "spring-data"],
|
|
63
|
+
criteria: [
|
|
64
|
+
"Order entity maps to orders table",
|
|
65
|
+
"Repository supports findByStatus",
|
|
66
|
+
"Flyway migration creates schema"
|
|
67
|
+
],
|
|
68
|
+
body: "Define Order JPA entity with audit fields, OrderRepository interface, and initial Flyway migration script."
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
title: "Configure Spring Security with JWT authentication",
|
|
72
|
+
tags: ["security", "auth"],
|
|
73
|
+
capabilities: ["java", "spring-security"],
|
|
74
|
+
criteria: [
|
|
75
|
+
"Unauthenticated requests return 401",
|
|
76
|
+
"Valid JWT grants access",
|
|
77
|
+
"Tokens expire after configured TTL"
|
|
78
|
+
],
|
|
79
|
+
body: "Set up Spring Security filter chain with JWT validation, configure public and protected endpoints."
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
title: "Write unit tests for OrderService",
|
|
83
|
+
tags: ["testing", "unit-test"],
|
|
84
|
+
capabilities: ["java", "testing"],
|
|
85
|
+
criteria: [
|
|
86
|
+
"Service methods have test coverage",
|
|
87
|
+
"Edge cases tested",
|
|
88
|
+
"Mocks used for repository layer"
|
|
89
|
+
],
|
|
90
|
+
body: "Create JUnit 5 tests for OrderService using Mockito for repository dependencies."
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
title: "Add pagination and sorting to list endpoints",
|
|
94
|
+
tags: ["rest", "api"],
|
|
95
|
+
capabilities: ["java", "spring-boot"],
|
|
96
|
+
criteria: [
|
|
97
|
+
"GET /api/orders supports page and size params",
|
|
98
|
+
"Sort by createdAt default",
|
|
99
|
+
"Response includes total count"
|
|
100
|
+
],
|
|
101
|
+
body: "Implement Spring Data Pageable support in OrderController with proper response envelope."
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
title: "Set up Docker container and Compose file",
|
|
105
|
+
tags: ["devops", "docker"],
|
|
106
|
+
capabilities: ["docker", "devops"],
|
|
107
|
+
criteria: [
|
|
108
|
+
"Dockerfile builds multi-stage image",
|
|
109
|
+
"docker-compose starts app with PostgreSQL",
|
|
110
|
+
"Container health check passes"
|
|
111
|
+
],
|
|
112
|
+
body: "Create optimized multi-stage Dockerfile and docker-compose.yml with PostgreSQL service."
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
title: "Implement global exception handler",
|
|
116
|
+
tags: ["rest", "error-handling"],
|
|
117
|
+
capabilities: ["java", "spring-boot"],
|
|
118
|
+
criteria: [
|
|
119
|
+
"Validation errors return structured response",
|
|
120
|
+
"404 returns proper body",
|
|
121
|
+
"500 errors are logged"
|
|
122
|
+
],
|
|
123
|
+
body: "Create @ControllerAdvice with handlers for common exceptions, returning consistent error response format."
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
title: "Add integration tests with TestContainers",
|
|
127
|
+
tags: ["testing", "integration"],
|
|
128
|
+
capabilities: ["java", "testing", "docker"],
|
|
129
|
+
criteria: [
|
|
130
|
+
"Tests run against real PostgreSQL",
|
|
131
|
+
"Database is reset between tests",
|
|
132
|
+
"CI pipeline can run tests"
|
|
133
|
+
],
|
|
134
|
+
body: "Set up TestContainers with PostgreSQL for integration testing, create base test class with shared container."
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
title: "Configure structured logging with correlation IDs",
|
|
138
|
+
tags: ["observability", "logging"],
|
|
139
|
+
capabilities: ["java", "observability"],
|
|
140
|
+
criteria: [
|
|
141
|
+
"Logs include correlationId header",
|
|
142
|
+
"JSON log format in production",
|
|
143
|
+
"Request/response logged at DEBUG"
|
|
144
|
+
],
|
|
145
|
+
body: "Set up Logback with JSON encoder, MDC-based correlation ID propagation via servlet filter."
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
title: "Create OpenAPI specification",
|
|
149
|
+
tags: ["documentation", "api"],
|
|
150
|
+
capabilities: ["java", "documentation"],
|
|
151
|
+
criteria: [
|
|
152
|
+
"Swagger UI accessible at /swagger-ui",
|
|
153
|
+
"All endpoints documented",
|
|
154
|
+
"Schema examples included"
|
|
155
|
+
],
|
|
156
|
+
body: "Add springdoc-openapi dependency, annotate controllers with OpenAPI annotations."
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
title: "Implement rate limiting middleware",
|
|
160
|
+
tags: ["security", "performance"],
|
|
161
|
+
capabilities: ["java", "spring-boot"],
|
|
162
|
+
criteria: [
|
|
163
|
+
"Rate limit headers in response",
|
|
164
|
+
"429 returned when exceeded",
|
|
165
|
+
"Configurable per-endpoint limits"
|
|
166
|
+
],
|
|
167
|
+
body: "Add Bucket4j-based rate limiting filter with configurable limits per endpoint."
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
title: "Set up GitHub Actions CI pipeline",
|
|
171
|
+
tags: ["devops", "ci"],
|
|
172
|
+
capabilities: ["devops", "ci"],
|
|
173
|
+
criteria: [
|
|
174
|
+
"Build runs on push to main",
|
|
175
|
+
"Tests execute in CI",
|
|
176
|
+
"Docker image pushed on release"
|
|
177
|
+
],
|
|
178
|
+
body: "Create GitHub Actions workflow for build, test, and Docker image publishing."
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
title: "Add Actuator metrics and custom health indicators",
|
|
182
|
+
tags: ["observability", "monitoring"],
|
|
183
|
+
capabilities: ["java", "observability"],
|
|
184
|
+
criteria: [
|
|
185
|
+
"Custom health check for database",
|
|
186
|
+
"Business metrics exposed",
|
|
187
|
+
"Prometheus endpoint available"
|
|
188
|
+
],
|
|
189
|
+
body: "Configure Actuator with Prometheus exporter, add custom HealthIndicator and MeterBinder implementations."
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
title: "Implement order status workflow",
|
|
193
|
+
tags: ["business-logic", "workflow"],
|
|
194
|
+
capabilities: ["java", "spring-boot"],
|
|
195
|
+
criteria: [
|
|
196
|
+
"Status transitions are validated",
|
|
197
|
+
"Invalid transitions return 422",
|
|
198
|
+
"Status history is tracked"
|
|
199
|
+
],
|
|
200
|
+
body: "Create OrderStatusMachine with allowed transitions, persist status change events."
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
const mockIssue = (index: number): Readonly<Record<string, unknown>> => {
|
|
205
|
+
const template = issueTemplates[(index - 1) % issueTemplates.length]
|
|
206
|
+
return {
|
|
207
|
+
id: `demo-issue-${index}`,
|
|
208
|
+
title: template?.title ?? "",
|
|
209
|
+
priority: index <= 3 ? "high" : index <= 7 ? "medium" : "low",
|
|
210
|
+
assignedAgent: null,
|
|
211
|
+
requiredCapabilities: template?.capabilities ?? [],
|
|
212
|
+
blockedBy: [],
|
|
213
|
+
tags: template?.tags ?? [],
|
|
214
|
+
acceptanceCriteria: template?.criteria ?? [],
|
|
215
|
+
estimate: "2h",
|
|
216
|
+
proofOfWork: ["diff-stat"],
|
|
217
|
+
body: template?.body ?? ""
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const requestedIssueCount = (prompt: string): number => {
|
|
222
|
+
const matched = /(\d+)\s*issue/i.exec(prompt)?.[1]
|
|
223
|
+
return Math.min(matched === undefined ? 10 : Number(matched), 100)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export const mockStructuredResponse = (prompt: string, schema: JsonSchema): string => {
|
|
227
|
+
const schemaText = JSON.stringify(schema)
|
|
228
|
+
if (schemaText.includes('"summary"') && schemaText.includes('"issues"')) {
|
|
229
|
+
const count = requestedIssueCount(prompt)
|
|
230
|
+
return JSON.stringify({
|
|
231
|
+
summary: `Generated ${count} issue drafts for Spring Boot microservice demo.`,
|
|
232
|
+
issues: Array.from({ length: count }, (_, index) => mockIssue(index + 1))
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
return JSON.stringify({
|
|
236
|
+
result: "mock structured response"
|
|
237
|
+
})
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const lastUserPrompt = (messages: ReadonlyArray<Message>): string => {
|
|
241
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
242
|
+
const message = messages[index]
|
|
243
|
+
if (message?.role === "User") {
|
|
244
|
+
return message.content
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return ""
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export const makeMockProvider = (config: LlmConfig): ApiConnectorShape => {
|
|
251
|
+
const executeStructuredWithUsage = <A, E, RD, RE>(
|
|
252
|
+
prompt: string,
|
|
253
|
+
schema: Schema.ConstraintCodec<A, E, RD, RE>,
|
|
254
|
+
jsonSchema: JsonSchema
|
|
255
|
+
): Effect.Effect<StructuredResult<A>, LlmError, RD> =>
|
|
256
|
+
Effect.gen(function* () {
|
|
257
|
+
const value = yield* parseFromText(
|
|
258
|
+
mockStructuredResponse(prompt, jsonSchema),
|
|
259
|
+
schema,
|
|
260
|
+
jsonSchema
|
|
261
|
+
)
|
|
262
|
+
const result: StructuredResult<A> = [value, undefined, undefined]
|
|
263
|
+
return result
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
const executeStream = (_prompt: string): Stream.Stream<LlmChunk, LlmError> => {
|
|
267
|
+
const words = mockResponse().split(" ")
|
|
268
|
+
return Stream.fromIterable(
|
|
269
|
+
words.map((word, index) => {
|
|
270
|
+
const last = index === words.length - 1
|
|
271
|
+
return LlmChunk.make({
|
|
272
|
+
delta: `${index === 0 ? "" : " "}${word}`,
|
|
273
|
+
metadata: {
|
|
274
|
+
provider: "mock",
|
|
275
|
+
model: config.model
|
|
276
|
+
},
|
|
277
|
+
...(last ? { finishReason: "stop" } : {}),
|
|
278
|
+
...(last
|
|
279
|
+
? {
|
|
280
|
+
usage: TokenUsage.make({
|
|
281
|
+
prompt: 10,
|
|
282
|
+
completion: words.length,
|
|
283
|
+
total: 10 + words.length
|
|
284
|
+
})
|
|
285
|
+
}
|
|
286
|
+
: {})
|
|
287
|
+
})
|
|
288
|
+
})
|
|
289
|
+
).pipe(Stream.schedule(Schedule.spaced(Duration.millis(50))))
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
id: ConnectorIds.Mock,
|
|
294
|
+
kind: "Api",
|
|
295
|
+
capabilities: apiConnectorCapabilities(),
|
|
296
|
+
healthCheck: Effect.succeed(
|
|
297
|
+
HealthStatus.make({
|
|
298
|
+
availability: "Healthy",
|
|
299
|
+
authStatus: "Valid",
|
|
300
|
+
latency: Duration.zero
|
|
301
|
+
})
|
|
302
|
+
),
|
|
303
|
+
executeStream,
|
|
304
|
+
executeStreamWithHistory: (messages) => executeStream(lastUserPrompt(messages)),
|
|
305
|
+
executeWithTools: (_prompt) =>
|
|
306
|
+
Effect.succeed(
|
|
307
|
+
ToolCallResponse.make({
|
|
308
|
+
content: mockResponse(),
|
|
309
|
+
toolCalls: [],
|
|
310
|
+
finishReason: "stop"
|
|
311
|
+
})
|
|
312
|
+
),
|
|
313
|
+
executeStructured: <A, E, RD, RE>(
|
|
314
|
+
prompt: string,
|
|
315
|
+
schema: Schema.ConstraintCodec<A, E, RD, RE>,
|
|
316
|
+
jsonSchema: JsonSchema
|
|
317
|
+
): Effect.Effect<A, LlmError, RD> =>
|
|
318
|
+
Effect.map(executeStructuredWithUsage(prompt, schema, jsonSchema), ([value]) => value),
|
|
319
|
+
executeStructuredWithUsage,
|
|
320
|
+
isAvailable: Effect.succeed(true)
|
|
321
|
+
}
|
|
322
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema"
|
|
2
|
+
|
|
3
|
+
export class OllamaOptions extends Schema.Class<OllamaOptions>("OllamaOptions")({
|
|
4
|
+
temperature: Schema.optionalKey(Schema.Number),
|
|
5
|
+
num_predict: Schema.optionalKey(Schema.Int)
|
|
6
|
+
}) {}
|
|
7
|
+
|
|
8
|
+
export class OllamaGenerateRequest extends Schema.Class<OllamaGenerateRequest>(
|
|
9
|
+
"OllamaGenerateRequest"
|
|
10
|
+
)({
|
|
11
|
+
model: Schema.String,
|
|
12
|
+
prompt: Schema.String,
|
|
13
|
+
stream: Schema.Boolean,
|
|
14
|
+
format: Schema.optionalKey(Schema.String),
|
|
15
|
+
options: Schema.optionalKey(OllamaOptions)
|
|
16
|
+
}) {}
|
|
17
|
+
|
|
18
|
+
export class OllamaMessage extends Schema.Class<OllamaMessage>("OllamaMessage")({
|
|
19
|
+
role: Schema.String,
|
|
20
|
+
content: Schema.String
|
|
21
|
+
}) {}
|
|
22
|
+
|
|
23
|
+
export class OllamaChatRequest extends Schema.Class<OllamaChatRequest>("OllamaChatRequest")({
|
|
24
|
+
model: Schema.String,
|
|
25
|
+
messages: Schema.Array(OllamaMessage),
|
|
26
|
+
stream: Schema.Boolean,
|
|
27
|
+
format: Schema.optionalKey(Schema.String),
|
|
28
|
+
options: Schema.optionalKey(OllamaOptions)
|
|
29
|
+
}) {}
|
|
30
|
+
|
|
31
|
+
export class OllamaGenerateResponse extends Schema.Class<OllamaGenerateResponse>(
|
|
32
|
+
"OllamaGenerateResponse"
|
|
33
|
+
)({
|
|
34
|
+
model: Schema.String,
|
|
35
|
+
response: Schema.String,
|
|
36
|
+
done: Schema.Boolean,
|
|
37
|
+
context: Schema.optionalKey(Schema.Array(Schema.Int)),
|
|
38
|
+
total_duration: Schema.optionalKey(Schema.Int),
|
|
39
|
+
load_duration: Schema.optionalKey(Schema.Int),
|
|
40
|
+
prompt_eval_count: Schema.optionalKey(Schema.Int),
|
|
41
|
+
eval_count: Schema.optionalKey(Schema.Int)
|
|
42
|
+
}) {}
|
|
43
|
+
|
|
44
|
+
export class OllamaChatResponse extends Schema.Class<OllamaChatResponse>("OllamaChatResponse")({
|
|
45
|
+
model: Schema.String,
|
|
46
|
+
message: OllamaMessage,
|
|
47
|
+
done: Schema.Boolean,
|
|
48
|
+
total_duration: Schema.optionalKey(Schema.Int),
|
|
49
|
+
load_duration: Schema.optionalKey(Schema.Int),
|
|
50
|
+
prompt_eval_count: Schema.optionalKey(Schema.Int),
|
|
51
|
+
eval_count: Schema.optionalKey(Schema.Int)
|
|
52
|
+
}) {}
|
|
53
|
+
|
|
54
|
+
export class OllamaModelInfo extends Schema.Class<OllamaModelInfo>("OllamaModelInfo")({
|
|
55
|
+
name: Schema.String,
|
|
56
|
+
modified_at: Schema.String,
|
|
57
|
+
size: Schema.Int
|
|
58
|
+
}) {}
|
|
59
|
+
|
|
60
|
+
export class OllamaModelsResponse extends Schema.Class<OllamaModelsResponse>(
|
|
61
|
+
"OllamaModelsResponse"
|
|
62
|
+
)({
|
|
63
|
+
models: Schema.Array(OllamaModelInfo)
|
|
64
|
+
}) {}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect"
|
|
2
|
+
import * as Schema from "effect/Schema"
|
|
3
|
+
import * as Stream from "effect/Stream"
|
|
4
|
+
import { makeApiConnector, type ApiConnectorShape } from "../Connector.ts"
|
|
5
|
+
import { ConfigError, InvalidRequestError, ParseError, type LlmError } from "../Errors.ts"
|
|
6
|
+
import type { HttpClientShape } from "../HttpClient.ts"
|
|
7
|
+
import type { StructuredResult } from "../LlmService.ts"
|
|
8
|
+
import {
|
|
9
|
+
ConnectorIds,
|
|
10
|
+
LlmChunk,
|
|
11
|
+
TokenUsage,
|
|
12
|
+
type JsonSchema,
|
|
13
|
+
type LlmConfig,
|
|
14
|
+
type Message
|
|
15
|
+
} from "../Models.ts"
|
|
16
|
+
import { parseFromText } from "../StructuredOutput.ts"
|
|
17
|
+
import {
|
|
18
|
+
OllamaChatRequest,
|
|
19
|
+
OllamaChatResponse,
|
|
20
|
+
OllamaGenerateRequest,
|
|
21
|
+
OllamaGenerateResponse,
|
|
22
|
+
OllamaMessage,
|
|
23
|
+
OllamaOptions
|
|
24
|
+
} from "./OllamaModels.ts"
|
|
25
|
+
|
|
26
|
+
const noHeaders: Readonly<Record<string, string>> = Object.freeze({})
|
|
27
|
+
|
|
28
|
+
const baseUrlEffect = (config: LlmConfig): Effect.Effect<string, ConfigError> =>
|
|
29
|
+
config.baseUrl === undefined
|
|
30
|
+
? Effect.fail(
|
|
31
|
+
ConfigError.make({
|
|
32
|
+
message: "Missing baseUrl for Ollama provider"
|
|
33
|
+
})
|
|
34
|
+
)
|
|
35
|
+
: Effect.succeed(config.baseUrl.replace(/\/+$/, ""))
|
|
36
|
+
|
|
37
|
+
const options = (config: LlmConfig): OllamaOptions =>
|
|
38
|
+
OllamaOptions.make({
|
|
39
|
+
...(config.temperature === undefined ? {} : { temperature: config.temperature }),
|
|
40
|
+
...(config.maxTokens === undefined ? {} : { num_predict: config.maxTokens })
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
export const ollamaHistory = (messages: ReadonlyArray<Message>): ReadonlyArray<OllamaMessage> =>
|
|
44
|
+
messages.map((message) =>
|
|
45
|
+
OllamaMessage.make({
|
|
46
|
+
role:
|
|
47
|
+
message.role === "System" ? "system" : message.role === "Assistant" ? "assistant" : "user",
|
|
48
|
+
content: message.content
|
|
49
|
+
})
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
const generateUsage = (response: OllamaGenerateResponse): TokenUsage | undefined =>
|
|
53
|
+
response.prompt_eval_count === undefined || response.eval_count === undefined
|
|
54
|
+
? undefined
|
|
55
|
+
: TokenUsage.make({
|
|
56
|
+
prompt: response.prompt_eval_count,
|
|
57
|
+
completion: response.eval_count,
|
|
58
|
+
total: response.prompt_eval_count + response.eval_count
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const chatUsage = (response: OllamaChatResponse): TokenUsage | undefined =>
|
|
62
|
+
response.prompt_eval_count === undefined || response.eval_count === undefined
|
|
63
|
+
? undefined
|
|
64
|
+
: TokenUsage.make({
|
|
65
|
+
prompt: response.prompt_eval_count,
|
|
66
|
+
completion: response.eval_count,
|
|
67
|
+
total: response.prompt_eval_count + response.eval_count
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
const decodeGenerate = (
|
|
71
|
+
raw: string,
|
|
72
|
+
stream: boolean
|
|
73
|
+
): Effect.Effect<OllamaGenerateResponse, ParseError> =>
|
|
74
|
+
Schema.decodeUnknownEffect(Schema.fromJsonString(OllamaGenerateResponse))(raw).pipe(
|
|
75
|
+
Effect.mapError((error) =>
|
|
76
|
+
ParseError.make({
|
|
77
|
+
message:
|
|
78
|
+
`${stream ? "Failed to parse Ollama stream chunk" : "Failed to decode Ollama response"}: ` +
|
|
79
|
+
String(error),
|
|
80
|
+
raw
|
|
81
|
+
})
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
const decodeChat = (raw: string): Effect.Effect<OllamaChatResponse, ParseError> =>
|
|
86
|
+
Schema.decodeUnknownEffect(Schema.fromJsonString(OllamaChatResponse))(raw).pipe(
|
|
87
|
+
Effect.mapError((error) =>
|
|
88
|
+
ParseError.make({
|
|
89
|
+
message: `Failed to parse Ollama chat stream chunk: ${String(error)}`,
|
|
90
|
+
raw
|
|
91
|
+
})
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
export const makeOllamaProvider = (
|
|
96
|
+
config: LlmConfig,
|
|
97
|
+
httpClient: HttpClientShape
|
|
98
|
+
): ApiConnectorShape => {
|
|
99
|
+
const generateStream = (prompt: string, format?: string): Stream.Stream<LlmChunk, LlmError> =>
|
|
100
|
+
Stream.unwrap(
|
|
101
|
+
Effect.map(baseUrlEffect(config), (baseUrl) => {
|
|
102
|
+
const request = OllamaGenerateRequest.make({
|
|
103
|
+
model: config.model,
|
|
104
|
+
prompt,
|
|
105
|
+
stream: true,
|
|
106
|
+
options: options(config),
|
|
107
|
+
...(format === undefined ? {} : { format })
|
|
108
|
+
})
|
|
109
|
+
return httpClient
|
|
110
|
+
.postJsonStream(
|
|
111
|
+
`${baseUrl}/api/generate`,
|
|
112
|
+
JSON.stringify(request),
|
|
113
|
+
noHeaders,
|
|
114
|
+
config.timeout
|
|
115
|
+
)
|
|
116
|
+
.pipe(
|
|
117
|
+
Stream.filter((line) => line.trim().length > 0),
|
|
118
|
+
Stream.mapEffect((line) => decodeGenerate(line, true)),
|
|
119
|
+
Stream.flatMap((response) => {
|
|
120
|
+
const usage = response.done ? generateUsage(response) : undefined
|
|
121
|
+
return response.response.length > 0 || response.done
|
|
122
|
+
? Stream.succeed(
|
|
123
|
+
LlmChunk.make({
|
|
124
|
+
delta: response.response,
|
|
125
|
+
metadata: {
|
|
126
|
+
provider: "ollama",
|
|
127
|
+
model: response.model
|
|
128
|
+
},
|
|
129
|
+
...(response.done ? { finishReason: "stop" } : {}),
|
|
130
|
+
...(usage === undefined ? {} : { usage })
|
|
131
|
+
})
|
|
132
|
+
)
|
|
133
|
+
: Stream.empty
|
|
134
|
+
})
|
|
135
|
+
)
|
|
136
|
+
})
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
const chatStream = (messages: ReadonlyArray<OllamaMessage>): Stream.Stream<LlmChunk, LlmError> =>
|
|
140
|
+
Stream.unwrap(
|
|
141
|
+
Effect.map(baseUrlEffect(config), (baseUrl) => {
|
|
142
|
+
const request = OllamaChatRequest.make({
|
|
143
|
+
model: config.model,
|
|
144
|
+
messages,
|
|
145
|
+
stream: true,
|
|
146
|
+
options: options(config)
|
|
147
|
+
})
|
|
148
|
+
return httpClient
|
|
149
|
+
.postJsonStream(`${baseUrl}/api/chat`, JSON.stringify(request), noHeaders, config.timeout)
|
|
150
|
+
.pipe(
|
|
151
|
+
Stream.filter((line) => line.trim().length > 0),
|
|
152
|
+
Stream.mapEffect(decodeChat),
|
|
153
|
+
Stream.flatMap((response) => {
|
|
154
|
+
const usage = response.done ? chatUsage(response) : undefined
|
|
155
|
+
return response.message.content.length > 0 || response.done
|
|
156
|
+
? Stream.succeed(
|
|
157
|
+
LlmChunk.make({
|
|
158
|
+
delta: response.message.content,
|
|
159
|
+
metadata: {
|
|
160
|
+
provider: "ollama",
|
|
161
|
+
model: response.model
|
|
162
|
+
},
|
|
163
|
+
...(response.done ? { finishReason: "stop" } : {}),
|
|
164
|
+
...(usage === undefined ? {} : { usage })
|
|
165
|
+
})
|
|
166
|
+
)
|
|
167
|
+
: Stream.empty
|
|
168
|
+
})
|
|
169
|
+
)
|
|
170
|
+
})
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
const generate = (
|
|
174
|
+
prompt: string,
|
|
175
|
+
format?: string
|
|
176
|
+
): Effect.Effect<readonly [response: OllamaGenerateResponse, raw: string], LlmError> =>
|
|
177
|
+
Effect.gen(function* () {
|
|
178
|
+
const baseUrl = yield* baseUrlEffect(config)
|
|
179
|
+
const request = OllamaGenerateRequest.make({
|
|
180
|
+
model: config.model,
|
|
181
|
+
prompt,
|
|
182
|
+
stream: false,
|
|
183
|
+
options: options(config),
|
|
184
|
+
...(format === undefined ? {} : { format })
|
|
185
|
+
})
|
|
186
|
+
const raw = yield* httpClient.postJson(
|
|
187
|
+
`${baseUrl}/api/generate`,
|
|
188
|
+
JSON.stringify(request),
|
|
189
|
+
noHeaders,
|
|
190
|
+
config.timeout
|
|
191
|
+
)
|
|
192
|
+
const response = yield* decodeGenerate(raw, false)
|
|
193
|
+
return [response, raw]
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
const executeStructuredWithUsage = <A, E, RD, RE>(
|
|
197
|
+
prompt: string,
|
|
198
|
+
schema: Schema.ConstraintCodec<A, E, RD, RE>,
|
|
199
|
+
jsonSchema: JsonSchema
|
|
200
|
+
): Effect.Effect<StructuredResult<A>, LlmError, RD> =>
|
|
201
|
+
Effect.gen(function* () {
|
|
202
|
+
const [response] = yield* generate(
|
|
203
|
+
`${prompt}\n\nPlease respond with valid JSON matching the provided schema.`,
|
|
204
|
+
"json"
|
|
205
|
+
)
|
|
206
|
+
const value = yield* parseFromText(response.response, schema, jsonSchema)
|
|
207
|
+
const result: StructuredResult<A> = [value, undefined, undefined]
|
|
208
|
+
return result
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
const isAvailable: Effect.Effect<boolean> =
|
|
212
|
+
config.baseUrl === undefined
|
|
213
|
+
? Effect.succeed(false)
|
|
214
|
+
: httpClient
|
|
215
|
+
.get(`${config.baseUrl.replace(/\/+$/, "")}/api/tags`, noHeaders, config.timeout)
|
|
216
|
+
.pipe(
|
|
217
|
+
Effect.match({
|
|
218
|
+
onFailure: () => false,
|
|
219
|
+
onSuccess: () => true
|
|
220
|
+
})
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
return makeApiConnector({
|
|
224
|
+
id: ConnectorIds.Ollama,
|
|
225
|
+
executeStream: (prompt) => generateStream(prompt),
|
|
226
|
+
executeStreamWithHistory: (messages) => chatStream(ollamaHistory(messages)),
|
|
227
|
+
executeWithTools: () =>
|
|
228
|
+
Effect.fail(
|
|
229
|
+
InvalidRequestError.make({
|
|
230
|
+
message: "Ollama provider does not support tool calling"
|
|
231
|
+
})
|
|
232
|
+
),
|
|
233
|
+
executeStructuredWithUsage,
|
|
234
|
+
isAvailable
|
|
235
|
+
})
|
|
236
|
+
}
|