@llm4ts/js 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/Client.d.ts +53 -0
- package/dist/Client.d.ts.map +1 -0
- package/dist/Client.js +143 -0
- package/dist/Client.js.map +1 -0
- package/dist/Package.d.ts +2 -0
- package/dist/Package.d.ts.map +1 -0
- package/dist/Package.js +2 -0
- package/dist/Package.js.map +1 -0
- package/package.json +57 -0
- package/src/Client.ts +204 -0
- package/src/Package.ts +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Riccardo Merolla
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @llm4ts/js
|
|
2
|
+
|
|
3
|
+
The fastest way to try [llm4ts](https://github.com/riccardomerolla/llm4ts):
|
|
4
|
+
a Promise-based client for plain JavaScript and TypeScript consumers — no
|
|
5
|
+
Effect knowledge required.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @llm4ts/js
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requires Node.js >= 22.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
Works out of the box with the built-in mock provider — no credentials needed:
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
import { createClient } from "@llm4ts/js"
|
|
21
|
+
|
|
22
|
+
const client = createClient({ provider: "mock", model: "mock" })
|
|
23
|
+
const response = await client.complete("Say hello")
|
|
24
|
+
console.log(response.content)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Swap in a real provider by name (`openai`, `anthropic`, `gemini`,
|
|
28
|
+
`lm-studio`, `ollama`) — API keys are read from the standard environment
|
|
29
|
+
variables (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`):
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
const client = createClient({
|
|
33
|
+
provider: "anthropic",
|
|
34
|
+
model: "claude-sonnet-4-5"
|
|
35
|
+
})
|
|
36
|
+
const answer = await client.complete("Summarize the Effect library in one line")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The client also exposes `health()` for checking provider availability and
|
|
40
|
+
credentials.
|
|
41
|
+
|
|
42
|
+
For full flows — plans, review loops, Git integration, CLI coding agents —
|
|
43
|
+
use [`@llm4ts/runner`](https://www.npmjs.com/package/@llm4ts/runner).
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
MIT
|
package/dist/Client.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
export declare const JsProvider: Schema.Literals<readonly ["mock", "openai", "anthropic", "gemini", "lm-studio", "ollama"]>;
|
|
3
|
+
export type JsProvider = typeof JsProvider.Type;
|
|
4
|
+
declare const ClientConfig_base: Schema.Class<ClientConfig, Schema.Struct<{
|
|
5
|
+
readonly provider: Schema.Literals<readonly ["mock", "openai", "anthropic", "gemini", "lm-studio", "ollama"]>;
|
|
6
|
+
readonly model: Schema.optionalKey<Schema.String>;
|
|
7
|
+
readonly baseUrl: Schema.optionalKey<Schema.String>;
|
|
8
|
+
readonly apiKey: Schema.optionalKey<Schema.String>;
|
|
9
|
+
readonly timeoutSeconds: Schema.optionalKey<Schema.Number>;
|
|
10
|
+
readonly temperature: Schema.optionalKey<Schema.Number>;
|
|
11
|
+
readonly maxTokens: Schema.optionalKey<Schema.Int>;
|
|
12
|
+
}>, {}>;
|
|
13
|
+
export declare class ClientConfig extends ClientConfig_base {
|
|
14
|
+
}
|
|
15
|
+
declare const CompletionUsage_base: Schema.Class<CompletionUsage, Schema.Struct<{
|
|
16
|
+
readonly prompt: Schema.Int;
|
|
17
|
+
readonly completion: Schema.Int;
|
|
18
|
+
readonly total: Schema.Int;
|
|
19
|
+
readonly cached: Schema.optionalKey<Schema.Int>;
|
|
20
|
+
}>, {}>;
|
|
21
|
+
export declare class CompletionUsage extends CompletionUsage_base {
|
|
22
|
+
}
|
|
23
|
+
declare const Completion_base: Schema.Class<Completion, Schema.Struct<{
|
|
24
|
+
readonly content: Schema.String;
|
|
25
|
+
readonly usage: Schema.optionalKey<typeof CompletionUsage>;
|
|
26
|
+
readonly metadata: Schema.$Record<Schema.String, Schema.String>;
|
|
27
|
+
}>, {}>;
|
|
28
|
+
export declare class Completion extends Completion_base {
|
|
29
|
+
}
|
|
30
|
+
declare const ClientHealth_base: Schema.Class<ClientHealth, Schema.Struct<{
|
|
31
|
+
readonly available: Schema.Boolean;
|
|
32
|
+
readonly availability: Schema.String;
|
|
33
|
+
readonly authentication: Schema.String;
|
|
34
|
+
}>, {}>;
|
|
35
|
+
export declare class ClientHealth extends ClientHealth_base {
|
|
36
|
+
}
|
|
37
|
+
export type ErrorCategory = "Configuration" | "Authentication" | "RateLimit" | "Timeout" | "Parse" | "Provider" | "Interrupted" | "Unknown";
|
|
38
|
+
export declare class Llm4tsError extends Error {
|
|
39
|
+
readonly category: ErrorCategory;
|
|
40
|
+
readonly detail: unknown;
|
|
41
|
+
constructor(category: ErrorCategory, message: string, detail?: unknown);
|
|
42
|
+
}
|
|
43
|
+
export interface CallOptions {
|
|
44
|
+
readonly signal?: AbortSignal;
|
|
45
|
+
}
|
|
46
|
+
export interface LlmClient {
|
|
47
|
+
readonly complete: (prompt: string, options?: CallOptions) => Promise<Completion>;
|
|
48
|
+
readonly health: (options?: CallOptions) => Promise<ClientHealth>;
|
|
49
|
+
}
|
|
50
|
+
export declare const createClient: (input: unknown) => LlmClient;
|
|
51
|
+
export declare const mockClient: () => LlmClient;
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=Client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Client.d.ts","sourceRoot":"","sources":["../src/Client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAavC,eAAO,MAAM,UAAU,4FAOrB,CAAA;AACF,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,IAAI,CAAA;;;;;;;;;;AAE/C,qBAAa,YAAa,SAAQ,iBAQhC;CAAG;;;;;;;AAEL,qBAAa,eAAgB,SAAQ,oBAKnC;CAAG;;;;;;AAEL,qBAAa,UAAW,SAAQ,eAI9B;CAAG;;;;;;AAEL,qBAAa,YAAa,SAAQ,iBAIhC;CAAG;AAEL,MAAM,MAAM,aAAa,GACrB,eAAe,GACf,gBAAgB,GAChB,WAAW,GACX,SAAS,GACT,OAAO,GACP,UAAU,GACV,aAAa,GACb,SAAS,CAAA;AAEb,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAA;IAChC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;gBAEZ,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO;CAMvE;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;IACjF,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,CAAA;CAClE;AAoFD,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,SAiC7C,CAAA;AAED,eAAO,MAAM,UAAU,QAAO,SAA8D,CAAA"}
|
package/dist/Client.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import * as Duration from "effect/Duration";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Redacted from "effect/Redacted";
|
|
4
|
+
import * as Schema from "effect/Schema";
|
|
5
|
+
import { ApiConnectorConfig } from "@llm4ts/core/ConnectorConfig";
|
|
6
|
+
import { AuthenticationError, ConfigError, ParseError, RateLimitError, TimeoutError } from "@llm4ts/core/Errors";
|
|
7
|
+
import { ConnectorIds } from "@llm4ts/core/Models";
|
|
8
|
+
import { collect } from "@llm4ts/core/Streaming";
|
|
9
|
+
import { nodeFlowRunnerDependencies } from "@llm4ts/runner/FlowRunner";
|
|
10
|
+
export const JsProvider = Schema.Literals([
|
|
11
|
+
"mock",
|
|
12
|
+
"openai",
|
|
13
|
+
"anthropic",
|
|
14
|
+
"gemini",
|
|
15
|
+
"lm-studio",
|
|
16
|
+
"ollama"
|
|
17
|
+
]);
|
|
18
|
+
export class ClientConfig extends Schema.Class("ClientConfig")({
|
|
19
|
+
provider: JsProvider,
|
|
20
|
+
model: Schema.optionalKey(Schema.String),
|
|
21
|
+
baseUrl: Schema.optionalKey(Schema.String),
|
|
22
|
+
apiKey: Schema.optionalKey(Schema.String),
|
|
23
|
+
timeoutSeconds: Schema.optionalKey(Schema.Number),
|
|
24
|
+
temperature: Schema.optionalKey(Schema.Number),
|
|
25
|
+
maxTokens: Schema.optionalKey(Schema.Int)
|
|
26
|
+
}) {
|
|
27
|
+
}
|
|
28
|
+
export class CompletionUsage extends Schema.Class("CompletionUsage")({
|
|
29
|
+
prompt: Schema.Int,
|
|
30
|
+
completion: Schema.Int,
|
|
31
|
+
total: Schema.Int,
|
|
32
|
+
cached: Schema.optionalKey(Schema.Int)
|
|
33
|
+
}) {
|
|
34
|
+
}
|
|
35
|
+
export class Completion extends Schema.Class("Completion")({
|
|
36
|
+
content: Schema.String,
|
|
37
|
+
usage: Schema.optionalKey(CompletionUsage),
|
|
38
|
+
metadata: Schema.Record(Schema.String, Schema.String)
|
|
39
|
+
}) {
|
|
40
|
+
}
|
|
41
|
+
export class ClientHealth extends Schema.Class("ClientHealth")({
|
|
42
|
+
available: Schema.Boolean,
|
|
43
|
+
availability: Schema.String,
|
|
44
|
+
authentication: Schema.String
|
|
45
|
+
}) {
|
|
46
|
+
}
|
|
47
|
+
export class Llm4tsError extends Error {
|
|
48
|
+
category;
|
|
49
|
+
detail;
|
|
50
|
+
constructor(category, message, detail) {
|
|
51
|
+
super(message, { cause: detail });
|
|
52
|
+
this.name = "Llm4tsError";
|
|
53
|
+
this.category = category;
|
|
54
|
+
this.detail = detail;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const connectorId = (provider) => {
|
|
58
|
+
switch (provider) {
|
|
59
|
+
case "mock":
|
|
60
|
+
return ConnectorIds.Mock;
|
|
61
|
+
case "openai":
|
|
62
|
+
return ConnectorIds.OpenAI;
|
|
63
|
+
case "anthropic":
|
|
64
|
+
return ConnectorIds.Anthropic;
|
|
65
|
+
case "gemini":
|
|
66
|
+
return ConnectorIds.GeminiApi;
|
|
67
|
+
case "lm-studio":
|
|
68
|
+
return ConnectorIds.LmStudio;
|
|
69
|
+
case "ollama":
|
|
70
|
+
return ConnectorIds.Ollama;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const categoryOf = (error) => error instanceof ConfigError
|
|
74
|
+
? "Configuration"
|
|
75
|
+
: error instanceof AuthenticationError
|
|
76
|
+
? "Authentication"
|
|
77
|
+
: error instanceof RateLimitError
|
|
78
|
+
? "RateLimit"
|
|
79
|
+
: error instanceof TimeoutError
|
|
80
|
+
? "Timeout"
|
|
81
|
+
: error instanceof ParseError
|
|
82
|
+
? "Parse"
|
|
83
|
+
: "Provider";
|
|
84
|
+
const messageOf = (error) => error instanceof Error && error.message.length > 0 ? error.message : String(error);
|
|
85
|
+
const bridge = async (effect, options) => {
|
|
86
|
+
try {
|
|
87
|
+
const result = await Effect.runPromise(Effect.result(effect), {
|
|
88
|
+
...(options?.signal === undefined ? {} : { signal: options.signal })
|
|
89
|
+
});
|
|
90
|
+
if (result._tag === "Failure") {
|
|
91
|
+
const failure = result.failure;
|
|
92
|
+
const category = categoryOf(failure);
|
|
93
|
+
throw new Llm4tsError(category, messageOf(failure), failure);
|
|
94
|
+
}
|
|
95
|
+
return result.success;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (error instanceof Llm4tsError) {
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
const interrupted = options?.signal?.aborted === true;
|
|
102
|
+
throw new Llm4tsError(interrupted ? "Interrupted" : "Unknown", interrupted ? "operation interrupted" : messageOf(error), error);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const decodeConfig = (input) => {
|
|
106
|
+
try {
|
|
107
|
+
return Schema.decodeUnknownSync(ClientConfig)(input);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
throw new Llm4tsError("Configuration", `invalid client configuration: ${messageOf(error)}`, error);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const apiConfig = (config) => ApiConnectorConfig.make({
|
|
114
|
+
connectorId: connectorId(config.provider),
|
|
115
|
+
model: config.model ?? (config.provider === "mock" ? "mock" : ""),
|
|
116
|
+
...(config.baseUrl === undefined ? {} : { baseUrl: config.baseUrl }),
|
|
117
|
+
...(config.apiKey === undefined ? {} : { apiKey: Redacted.make(config.apiKey) }),
|
|
118
|
+
...(config.timeoutSeconds === undefined
|
|
119
|
+
? {}
|
|
120
|
+
: { timeout: Duration.seconds(config.timeoutSeconds) }),
|
|
121
|
+
...(config.temperature === undefined ? {} : { temperature: config.temperature }),
|
|
122
|
+
...(config.maxTokens === undefined ? {} : { maxTokens: config.maxTokens })
|
|
123
|
+
});
|
|
124
|
+
export const createClient = (input) => {
|
|
125
|
+
const config = apiConfig(decodeConfig(input));
|
|
126
|
+
const connector = nodeFlowRunnerDependencies().registry.resolve(config);
|
|
127
|
+
return {
|
|
128
|
+
complete: (prompt, options) => bridge(Effect.flatMap(connector, (service) => collect(service.executeStream(prompt))).pipe(Effect.map((response) => Completion.make({
|
|
129
|
+
content: response.content,
|
|
130
|
+
metadata: response.metadata,
|
|
131
|
+
...(response.usage === undefined
|
|
132
|
+
? {}
|
|
133
|
+
: { usage: CompletionUsage.make(response.usage) })
|
|
134
|
+
}))), options),
|
|
135
|
+
health: (options) => bridge(Effect.flatMap(connector, (service) => service.healthCheck).pipe(Effect.map((health) => ClientHealth.make({
|
|
136
|
+
available: health.availability === "Healthy" || health.availability === "Degraded",
|
|
137
|
+
availability: health.availability,
|
|
138
|
+
authentication: health.authStatus
|
|
139
|
+
}))), options)
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
export const mockClient = () => createClient({ provider: "mock", model: "mock" });
|
|
143
|
+
//# sourceMappingURL=Client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Client.js","sourceRoot":"","sources":["../src/Client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Package.d.ts","sourceRoot":"","sources":["../src/Package.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,eAAe,CAAA"}
|
package/dist/Package.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Package.js","sourceRoot":"","sources":["../src/Package.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,YAAY,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@llm4ts/js",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Promise and exception facade for llm4ts",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"src"
|
|
14
|
+
],
|
|
15
|
+
"exports": {
|
|
16
|
+
".": "./dist/Client.js",
|
|
17
|
+
"./Client": "./dist/Client.js",
|
|
18
|
+
"./Package": "./dist/Package.js",
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public",
|
|
23
|
+
"provenance": true
|
|
24
|
+
},
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/riccardomerolla/llm4ts.git",
|
|
28
|
+
"directory": "packages/js"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/riccardomerolla/llm4ts#readme",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/riccardomerolla/llm4ts/issues"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"llm",
|
|
36
|
+
"effect",
|
|
37
|
+
"ai",
|
|
38
|
+
"agents",
|
|
39
|
+
"workflow",
|
|
40
|
+
"typescript"
|
|
41
|
+
],
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@llm4ts/flow": "0.1.0",
|
|
44
|
+
"@llm4ts/core": "0.1.0",
|
|
45
|
+
"@llm4ts/runner": "0.1.0"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"effect": "^4.0.0-beta.102"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"effect": "^4.0.0-beta.102"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"clean": "tsc -b tsconfig.json --clean",
|
|
55
|
+
"test": "vitest run test"
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/Client.ts
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import * as Duration from "effect/Duration"
|
|
2
|
+
import * as Effect from "effect/Effect"
|
|
3
|
+
import * as Redacted from "effect/Redacted"
|
|
4
|
+
import * as Schema from "effect/Schema"
|
|
5
|
+
import { ApiConnectorConfig } from "@llm4ts/core/ConnectorConfig"
|
|
6
|
+
import {
|
|
7
|
+
AuthenticationError,
|
|
8
|
+
ConfigError,
|
|
9
|
+
ParseError,
|
|
10
|
+
RateLimitError,
|
|
11
|
+
TimeoutError
|
|
12
|
+
} from "@llm4ts/core/Errors"
|
|
13
|
+
import { ConnectorIds, type ConnectorId } from "@llm4ts/core/Models"
|
|
14
|
+
import { collect } from "@llm4ts/core/Streaming"
|
|
15
|
+
import { nodeFlowRunnerDependencies } from "@llm4ts/runner/FlowRunner"
|
|
16
|
+
|
|
17
|
+
export const JsProvider = Schema.Literals([
|
|
18
|
+
"mock",
|
|
19
|
+
"openai",
|
|
20
|
+
"anthropic",
|
|
21
|
+
"gemini",
|
|
22
|
+
"lm-studio",
|
|
23
|
+
"ollama"
|
|
24
|
+
])
|
|
25
|
+
export type JsProvider = typeof JsProvider.Type
|
|
26
|
+
|
|
27
|
+
export class ClientConfig extends Schema.Class<ClientConfig>("ClientConfig")({
|
|
28
|
+
provider: JsProvider,
|
|
29
|
+
model: Schema.optionalKey(Schema.String),
|
|
30
|
+
baseUrl: Schema.optionalKey(Schema.String),
|
|
31
|
+
apiKey: Schema.optionalKey(Schema.String),
|
|
32
|
+
timeoutSeconds: Schema.optionalKey(Schema.Number),
|
|
33
|
+
temperature: Schema.optionalKey(Schema.Number),
|
|
34
|
+
maxTokens: Schema.optionalKey(Schema.Int)
|
|
35
|
+
}) {}
|
|
36
|
+
|
|
37
|
+
export class CompletionUsage extends Schema.Class<CompletionUsage>("CompletionUsage")({
|
|
38
|
+
prompt: Schema.Int,
|
|
39
|
+
completion: Schema.Int,
|
|
40
|
+
total: Schema.Int,
|
|
41
|
+
cached: Schema.optionalKey(Schema.Int)
|
|
42
|
+
}) {}
|
|
43
|
+
|
|
44
|
+
export class Completion extends Schema.Class<Completion>("Completion")({
|
|
45
|
+
content: Schema.String,
|
|
46
|
+
usage: Schema.optionalKey(CompletionUsage),
|
|
47
|
+
metadata: Schema.Record(Schema.String, Schema.String)
|
|
48
|
+
}) {}
|
|
49
|
+
|
|
50
|
+
export class ClientHealth extends Schema.Class<ClientHealth>("ClientHealth")({
|
|
51
|
+
available: Schema.Boolean,
|
|
52
|
+
availability: Schema.String,
|
|
53
|
+
authentication: Schema.String
|
|
54
|
+
}) {}
|
|
55
|
+
|
|
56
|
+
export type ErrorCategory =
|
|
57
|
+
| "Configuration"
|
|
58
|
+
| "Authentication"
|
|
59
|
+
| "RateLimit"
|
|
60
|
+
| "Timeout"
|
|
61
|
+
| "Parse"
|
|
62
|
+
| "Provider"
|
|
63
|
+
| "Interrupted"
|
|
64
|
+
| "Unknown"
|
|
65
|
+
|
|
66
|
+
export class Llm4tsError extends Error {
|
|
67
|
+
readonly category: ErrorCategory
|
|
68
|
+
readonly detail: unknown
|
|
69
|
+
|
|
70
|
+
constructor(category: ErrorCategory, message: string, detail?: unknown) {
|
|
71
|
+
super(message, { cause: detail })
|
|
72
|
+
this.name = "Llm4tsError"
|
|
73
|
+
this.category = category
|
|
74
|
+
this.detail = detail
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface CallOptions {
|
|
79
|
+
readonly signal?: AbortSignal
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface LlmClient {
|
|
83
|
+
readonly complete: (prompt: string, options?: CallOptions) => Promise<Completion>
|
|
84
|
+
readonly health: (options?: CallOptions) => Promise<ClientHealth>
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const connectorId = (provider: JsProvider): ConnectorId => {
|
|
88
|
+
switch (provider) {
|
|
89
|
+
case "mock":
|
|
90
|
+
return ConnectorIds.Mock
|
|
91
|
+
case "openai":
|
|
92
|
+
return ConnectorIds.OpenAI
|
|
93
|
+
case "anthropic":
|
|
94
|
+
return ConnectorIds.Anthropic
|
|
95
|
+
case "gemini":
|
|
96
|
+
return ConnectorIds.GeminiApi
|
|
97
|
+
case "lm-studio":
|
|
98
|
+
return ConnectorIds.LmStudio
|
|
99
|
+
case "ollama":
|
|
100
|
+
return ConnectorIds.Ollama
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const categoryOf = (error: unknown): ErrorCategory =>
|
|
105
|
+
error instanceof ConfigError
|
|
106
|
+
? "Configuration"
|
|
107
|
+
: error instanceof AuthenticationError
|
|
108
|
+
? "Authentication"
|
|
109
|
+
: error instanceof RateLimitError
|
|
110
|
+
? "RateLimit"
|
|
111
|
+
: error instanceof TimeoutError
|
|
112
|
+
? "Timeout"
|
|
113
|
+
: error instanceof ParseError
|
|
114
|
+
? "Parse"
|
|
115
|
+
: "Provider"
|
|
116
|
+
|
|
117
|
+
const messageOf = (error: unknown): string =>
|
|
118
|
+
error instanceof Error && error.message.length > 0 ? error.message : String(error)
|
|
119
|
+
|
|
120
|
+
const bridge = async <A, E>(effect: Effect.Effect<A, E>, options?: CallOptions): Promise<A> => {
|
|
121
|
+
try {
|
|
122
|
+
const result = await Effect.runPromise(Effect.result(effect), {
|
|
123
|
+
...(options?.signal === undefined ? {} : { signal: options.signal })
|
|
124
|
+
})
|
|
125
|
+
if (result._tag === "Failure") {
|
|
126
|
+
const failure = result.failure
|
|
127
|
+
const category = categoryOf(failure)
|
|
128
|
+
throw new Llm4tsError(category, messageOf(failure), failure)
|
|
129
|
+
}
|
|
130
|
+
return result.success
|
|
131
|
+
} catch (error) {
|
|
132
|
+
if (error instanceof Llm4tsError) {
|
|
133
|
+
throw error
|
|
134
|
+
}
|
|
135
|
+
const interrupted = options?.signal?.aborted === true
|
|
136
|
+
throw new Llm4tsError(
|
|
137
|
+
interrupted ? "Interrupted" : "Unknown",
|
|
138
|
+
interrupted ? "operation interrupted" : messageOf(error),
|
|
139
|
+
error
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const decodeConfig = (input: unknown): ClientConfig => {
|
|
145
|
+
try {
|
|
146
|
+
return Schema.decodeUnknownSync(ClientConfig)(input)
|
|
147
|
+
} catch (error) {
|
|
148
|
+
throw new Llm4tsError(
|
|
149
|
+
"Configuration",
|
|
150
|
+
`invalid client configuration: ${messageOf(error)}`,
|
|
151
|
+
error
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const apiConfig = (config: ClientConfig): ApiConnectorConfig =>
|
|
157
|
+
ApiConnectorConfig.make({
|
|
158
|
+
connectorId: connectorId(config.provider),
|
|
159
|
+
model: config.model ?? (config.provider === "mock" ? "mock" : ""),
|
|
160
|
+
...(config.baseUrl === undefined ? {} : { baseUrl: config.baseUrl }),
|
|
161
|
+
...(config.apiKey === undefined ? {} : { apiKey: Redacted.make(config.apiKey) }),
|
|
162
|
+
...(config.timeoutSeconds === undefined
|
|
163
|
+
? {}
|
|
164
|
+
: { timeout: Duration.seconds(config.timeoutSeconds) }),
|
|
165
|
+
...(config.temperature === undefined ? {} : { temperature: config.temperature }),
|
|
166
|
+
...(config.maxTokens === undefined ? {} : { maxTokens: config.maxTokens })
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
export const createClient = (input: unknown): LlmClient => {
|
|
170
|
+
const config = apiConfig(decodeConfig(input))
|
|
171
|
+
const connector = nodeFlowRunnerDependencies().registry.resolve(config)
|
|
172
|
+
return {
|
|
173
|
+
complete: (prompt, options) =>
|
|
174
|
+
bridge(
|
|
175
|
+
Effect.flatMap(connector, (service) => collect(service.executeStream(prompt))).pipe(
|
|
176
|
+
Effect.map((response) =>
|
|
177
|
+
Completion.make({
|
|
178
|
+
content: response.content,
|
|
179
|
+
metadata: response.metadata,
|
|
180
|
+
...(response.usage === undefined
|
|
181
|
+
? {}
|
|
182
|
+
: { usage: CompletionUsage.make(response.usage) })
|
|
183
|
+
})
|
|
184
|
+
)
|
|
185
|
+
),
|
|
186
|
+
options
|
|
187
|
+
),
|
|
188
|
+
health: (options) =>
|
|
189
|
+
bridge(
|
|
190
|
+
Effect.flatMap(connector, (service) => service.healthCheck).pipe(
|
|
191
|
+
Effect.map((health) =>
|
|
192
|
+
ClientHealth.make({
|
|
193
|
+
available: health.availability === "Healthy" || health.availability === "Degraded",
|
|
194
|
+
availability: health.availability,
|
|
195
|
+
authentication: health.authStatus
|
|
196
|
+
})
|
|
197
|
+
)
|
|
198
|
+
),
|
|
199
|
+
options
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export const mockClient = (): LlmClient => createClient({ provider: "mock", model: "mock" })
|
package/src/Package.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const packageName = "@llm4ts/js"
|