@llm4ts/core 2.54.1 → 3.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 (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,45 +0,0 @@
1
- import * as Context from "effect/Context";
2
- import * as Effect from "effect/Effect";
3
- import * as Layer from "effect/Layer";
4
- import * as Schema from "effect/Schema";
5
- import type { LlmError } from "../Errors.ts";
6
- import type { ToolCall } from "../Models.ts";
7
- import type { JsonValue } from "../providers/CliSupport.ts";
8
- import { type Tool, type ToolExecutionError } from "./Tool.ts";
9
- declare const ToolSucceeded_base: Schema.Class<ToolSucceeded, Schema.TaggedStruct<"Succeeded", {
10
- readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
11
- }>, {}>;
12
- export declare class ToolSucceeded extends ToolSucceeded_base {
13
- }
14
- declare const ToolFailed_base: Schema.Class<ToolFailed, Schema.TaggedStruct<"Failed", {
15
- readonly message: Schema.String;
16
- }>, {}>;
17
- export declare class ToolFailed extends ToolFailed_base {
18
- }
19
- export declare const ToolResultValue: Schema.Union<readonly [typeof ToolSucceeded, typeof ToolFailed]>;
20
- export type ToolResultValue = typeof ToolResultValue.Type;
21
- declare const ToolResult_base: Schema.Class<ToolResult, Schema.Struct<{
22
- readonly toolCallId: Schema.String;
23
- readonly result: Schema.Union<readonly [typeof ToolSucceeded, typeof ToolFailed]>;
24
- readonly denied: Schema.withConstructorDefault<Schema.Boolean>;
25
- }>, {}>;
26
- export declare class ToolResult extends ToolResult_base {
27
- }
28
- export interface ToolRegistryShape {
29
- readonly register: (tool: Tool) => Effect.Effect<void, ToolExecutionError>;
30
- readonly registerAll: (tools: ReadonlyArray<Tool>) => Effect.Effect<void, ToolExecutionError>;
31
- readonly get: (name: string) => Effect.Effect<Tool, LlmError>;
32
- readonly list: Effect.Effect<ReadonlyArray<Tool>>;
33
- readonly select: (prompt: string, limit?: number) => Effect.Effect<ReadonlyArray<Tool>>;
34
- readonly validate: (call: ToolCall) => Effect.Effect<readonly [tool: Tool, parameters: JsonValue], LlmError>;
35
- readonly execute: (call: ToolCall) => Effect.Effect<ToolResult, LlmError>;
36
- readonly executeAll: (calls: ReadonlyArray<ToolCall>, concurrency?: number) => Effect.Effect<ReadonlyArray<ToolResult>, LlmError>;
37
- }
38
- declare const ToolRegistry_base: Context.ServiceClass<ToolRegistry, "@llm4ts/core/tools/ToolRegistry", ToolRegistryShape>;
39
- export declare class ToolRegistry extends ToolRegistry_base {
40
- }
41
- export declare const makeToolRegistry: () => Effect.Effect<ToolRegistryShape, never, never>;
42
- export declare const ToolRegistryLive: Layer.Layer<ToolRegistry, never, never>;
43
- export declare const toolResultValue: (result: ToolResult) => JsonValue;
44
- export {};
45
- //# sourceMappingURL=ToolRegistry.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ToolRegistry.d.ts","sourceRoot":"","sources":["../../src/tools/ToolRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAErC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAC3D,OAAO,EAGL,KAAK,IAAI,EACT,KAAK,kBAAkB,EACxB,MAAM,WAAW,CAAA;;;;AAElB,qBAAa,aAAc,SAAQ,kBAEjC;CAAG;;;;AAEL,qBAAa,UAAW,SAAQ,eAE9B;CAAG;AAEL,eAAO,MAAM,eAAe,kEAA4C,CAAA;AACxE,MAAM,MAAM,eAAe,GAAG,OAAO,eAAe,CAAC,IAAI,CAAA;;;;;;AAEzD,qBAAa,UAAW,SAAQ,eAI9B;CAAG;AAEL,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAA;IAC1E,QAAQ,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAA;IAC7F,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IAC7D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAA;IACjD,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAA;IACvF,QAAQ,CAAC,QAAQ,EAAE,CACjB,IAAI,EAAE,QAAQ,KACX,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAA;IAC1E,QAAQ,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;IACzE,QAAQ,CAAC,UAAU,EAAE,CACnB,KAAK,EAAE,aAAa,CAAC,QAAQ,CAAC,EAC9B,WAAW,CAAC,EAAE,MAAM,KACjB,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,CAAA;CACxD;;AAED,qBAAa,YAAa,SAAQ,iBAEjC;CAAG;AAiCJ,eAAO,MAAM,gBAAgB,sDAiI5B,CAAA;AAED,eAAO,MAAM,gBAAgB,yCAAiD,CAAA;AAE9E,eAAO,MAAM,eAAe,GAAI,QAAQ,UAAU,KAAG,SAK9C,CAAA"}
@@ -1,133 +0,0 @@
1
- import * as Context from "effect/Context";
2
- import * as Effect from "effect/Effect";
3
- import * as Layer from "effect/Layer";
4
- import * as Ref from "effect/Ref";
5
- import * as Schema from "effect/Schema";
6
- import { allows, currentGrants } from "../Capability.js";
7
- import { InvalidRequestError, ToolError } from "../Errors.js";
8
- import { DuplicateToolName, validateToolParameters } from "./Tool.js";
9
- export class ToolSucceeded extends Schema.TaggedClass()("Succeeded", {
10
- value: Schema.Json
11
- }) {
12
- }
13
- export class ToolFailed extends Schema.TaggedClass()("Failed", {
14
- message: Schema.String
15
- }) {
16
- }
17
- export const ToolResultValue = Schema.Union([ToolSucceeded, ToolFailed]);
18
- export class ToolResult extends Schema.Class("ToolResult")({
19
- toolCallId: Schema.String,
20
- result: ToolResultValue,
21
- denied: Schema.Boolean.pipe(Schema.withConstructorDefault(Effect.succeed(false)))
22
- }) {
23
- }
24
- export class ToolRegistry extends Context.Service()("@llm4ts/core/tools/ToolRegistry") {
25
- }
26
- const capabilityName = (capability) => capability._tag === "Exec" ? `Exec(${capability.command})` : capability._tag;
27
- const promptWords = (prompt) => new Set(prompt
28
- .toLowerCase()
29
- .split(/[^a-z0-9_]+/)
30
- .filter((word) => word.length > 0));
31
- const toolScore = (tool, words) => {
32
- let score = 0;
33
- for (const word of words) {
34
- score += tool.name.toLowerCase().includes(word) ? 5 : 0;
35
- score += [...tool.tags].some((tag) => tag.toLowerCase() === word) ? 4 : 0;
36
- score += tool.description.toLowerCase().includes(word) ? 2 : 0;
37
- }
38
- return score;
39
- };
40
- const parseArguments = (call) => Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Json))(call.arguments).pipe(Effect.mapError((error) => ToolError.make({
41
- toolName: call.name,
42
- detail: `Invalid arguments JSON: ${String(error)}`
43
- })));
44
- export const makeToolRegistry = Effect.fn("@llm4ts/core/tools/ToolRegistry.make")(function* () {
45
- const state = yield* Ref.make(new Map());
46
- const register = (tool) => Ref.modify(state, (current) => {
47
- if (current.has(tool.name)) {
48
- return [false, current];
49
- }
50
- const next = new Map(current);
51
- next.set(tool.name, tool);
52
- return [true, next];
53
- }).pipe(Effect.flatMap((registered) => registered ? Effect.void : Effect.fail(DuplicateToolName.make({ name: tool.name }))));
54
- const registerAll = (tools) => Effect.forEach(tools, register, {
55
- discard: true
56
- });
57
- const get = (name) => Effect.flatMap(Ref.get(state), (tools) => {
58
- const tool = tools.get(name);
59
- return tool === undefined
60
- ? Effect.fail(ToolError.make({
61
- toolName: name,
62
- detail: `Tool not found: ${name}`
63
- }))
64
- : Effect.succeed(tool);
65
- });
66
- const list = Ref.get(state).pipe(Effect.map((tools) => [...tools.values()].sort((left, right) => left.name.localeCompare(right.name))));
67
- const select = (prompt, limit = 8) => Effect.map(list, (tools) => tools
68
- .map((tool) => ({
69
- tool,
70
- score: toolScore(tool, promptWords(prompt))
71
- }))
72
- .filter(({ score }) => score > 0)
73
- .sort((left, right) => right.score - left.score || left.tool.name.localeCompare(right.tool.name))
74
- .slice(0, Math.max(0, limit))
75
- .map(({ tool }) => tool));
76
- const validate = Effect.fn("@llm4ts/core/tools/ToolRegistry.validate")(function* (call) {
77
- const tool = yield* get(call.name);
78
- const parameters = yield* parseArguments(call);
79
- yield* validateToolParameters(tool.parameters, parameters).pipe(Effect.mapError((error) => ToolError.make({
80
- toolName: tool.name,
81
- detail: error.message
82
- })));
83
- return [tool, parameters];
84
- });
85
- const execute = Effect.fn("@llm4ts/core/tools/ToolRegistry.execute")(function* (call) {
86
- const [tool, parameters] = yield* validate(call);
87
- const grants = yield* currentGrants;
88
- const missing = tool.requires
89
- .filter((capability) => !allows(grants, capability))
90
- .sort((left, right) => capabilityName(left).localeCompare(capabilityName(right)));
91
- if (missing.length > 0) {
92
- return ToolResult.make({
93
- toolCallId: call.id,
94
- denied: true,
95
- result: ToolFailed.make({
96
- message: `Tool '${tool.name}' requires ungranted capabilities: ` +
97
- missing.map(capabilityName).join(", ")
98
- })
99
- });
100
- }
101
- const result = yield* Effect.result(tool.execute(parameters));
102
- return ToolResult.make({
103
- toolCallId: call.id,
104
- result: result._tag === "Success"
105
- ? ToolSucceeded.make({ value: result.success })
106
- : ToolFailed.make({ message: result.failure.message })
107
- });
108
- });
109
- const executeAll = (calls, concurrency = 1) => !Number.isInteger(concurrency) || concurrency <= 0
110
- ? Effect.fail(InvalidRequestError.make({
111
- message: `Tool execution concurrency must be a positive integer; received ${concurrency}`
112
- }))
113
- : Effect.forEach(calls, execute, {
114
- concurrency
115
- });
116
- return {
117
- register,
118
- registerAll,
119
- get,
120
- list,
121
- select,
122
- validate,
123
- execute,
124
- executeAll
125
- };
126
- });
127
- export const ToolRegistryLive = Layer.effect(ToolRegistry, makeToolRegistry());
128
- export const toolResultValue = (result) => result.result._tag === "Succeeded"
129
- ? result.result.value
130
- : {
131
- error: result.result.message
132
- };
133
- //# sourceMappingURL=ToolRegistry.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ToolRegistry.js","sourceRoot":"","sources":["../../src/tools/ToolRegistry.ts"],"names":[],"mappings":"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"}
@@ -1,367 +0,0 @@
1
- import * as Crypto from "effect/Crypto"
2
- import * as DateTime from "effect/DateTime"
3
- import * as Effect from "effect/Effect"
4
- import * as Schema from "effect/Schema"
5
- import { ConversationMessage, type PromptRole } from "./Conversation.ts"
6
- import type { LlmProvider } from "./Models.ts"
7
-
8
- export class ContextInvalidInput extends Schema.TaggedError<ContextInvalidInput>()("InvalidInput", {
9
- message: Schema.String
10
- }) {}
11
-
12
- export class ContextParseFailed extends Schema.TaggedError<ContextParseFailed>()("ParseFailed", {
13
- message: Schema.String
14
- }) {}
15
-
16
- export class ContextSummarizationFailed extends Schema.TaggedError<ContextSummarizationFailed>()(
17
- "SummarizationFailed",
18
- {
19
- message: Schema.String
20
- }
21
- ) {}
22
-
23
- export class ContextToolLoopFailed extends Schema.TaggedError<ContextToolLoopFailed>()(
24
- "ToolLoopFailed",
25
- {
26
- message: Schema.String
27
- }
28
- ) {}
29
-
30
- export const ContextError = Schema.Union([
31
- ContextInvalidInput,
32
- ContextParseFailed,
33
- ContextSummarizationFailed,
34
- ContextToolLoopFailed
35
- ])
36
- export type ContextError = typeof ContextError.Type
37
-
38
- export class ContextLimits extends Schema.Class<ContextLimits>("ContextLimits")({
39
- maxTokens: Schema.Int,
40
- warningThresholdPct: Schema.Number.pipe(Schema.withConstructorDefault(Effect.succeed(0.85)))
41
- }) {}
42
-
43
- export class ContextWindow extends Schema.Class<ContextWindow>("ContextWindow")({
44
- messages: Schema.Array(ConversationMessage),
45
- totalTokens: Schema.Int,
46
- approachingLimit: Schema.Boolean,
47
- trimmed: Schema.Boolean
48
- }) {}
49
-
50
- export class DropOldestFifo extends Schema.TaggedClass<DropOldestFifo>()("DropOldestFifo", {}) {}
51
-
52
- export class SlidingWindow extends Schema.TaggedClass<SlidingWindow>()("SlidingWindow", {}) {}
53
-
54
- export class PriorityBased extends Schema.TaggedClass<PriorityBased>()("PriorityBased", {}) {}
55
-
56
- export class SummarizeOldMessages extends Schema.TaggedClass<SummarizeOldMessages>()(
57
- "SummarizeOldMessages",
58
- {
59
- summaryTargetTokens: Schema.Int.pipe(Schema.withConstructorDefault(Effect.succeed(256)))
60
- }
61
- ) {}
62
-
63
- export const ContextTrimmingStrategy = Schema.Union([
64
- DropOldestFifo,
65
- SlidingWindow,
66
- PriorityBased,
67
- SummarizeOldMessages
68
- ])
69
- export type ContextTrimmingStrategy = typeof ContextTrimmingStrategy.Type
70
-
71
- export const ContextTrimmingStrategies = Object.freeze({
72
- DropOldestFifo: DropOldestFifo.make({}),
73
- SlidingWindow: SlidingWindow.make({}),
74
- PriorityBased: PriorityBased.make({}),
75
- SummarizeOldMessages: (summaryTargetTokens = 256): SummarizeOldMessages =>
76
- SummarizeOldMessages.make({ summaryTargetTokens })
77
- })
78
-
79
- export interface TokenCounter {
80
- readonly countText: (provider: LlmProvider, text: string) => number
81
- readonly countMessage: (provider: LlmProvider, message: ConversationMessage) => number
82
- readonly countMessages: (provider: LlmProvider, messages: Iterable<ConversationMessage>) => number
83
- }
84
-
85
- const roleOverhead = (role: PromptRole): number => {
86
- switch (role) {
87
- case "System":
88
- return 6
89
- case "User":
90
- case "Assistant":
91
- return 5
92
- case "Tool":
93
- return 8
94
- }
95
- }
96
-
97
- const providerCharactersPerToken = (provider: LlmProvider): number => {
98
- switch (provider) {
99
- case "OpenAI":
100
- case "OpenCode":
101
- return 4
102
- case "Anthropic":
103
- return 3.8
104
- case "GeminiApi":
105
- case "GeminiCli":
106
- return 4.2
107
- case "LmStudio":
108
- case "Ollama":
109
- case "MlxLm":
110
- case "Mock":
111
- return 4.5
112
- }
113
- }
114
-
115
- const countText = (provider: LlmProvider, text: string): number =>
116
- Math.max(1, Math.ceil(Math.max(text.length, 1) / providerCharactersPerToken(provider)))
117
-
118
- const countMessage = (provider: LlmProvider, message: ConversationMessage): number =>
119
- countText(provider, message.content) + roleOverhead(message.role)
120
-
121
- export const defaultTokenCounter: TokenCounter = {
122
- countText,
123
- countMessage,
124
- countMessages: (provider, messages) =>
125
- Array.from(messages).reduce((total, message) => total + countMessage(provider, message), 0)
126
- }
127
-
128
- export interface MessageSummarizer<R = never> {
129
- readonly summarize: (
130
- messages: ReadonlyArray<ConversationMessage>,
131
- targetTokens: number
132
- ) => Effect.Effect<ConversationMessage, ContextError, R>
133
- }
134
-
135
- export const simpleMessageSummarizer: MessageSummarizer<Crypto.Crypto> = {
136
- summarize: (messages, targetTokens) => {
137
- if (messages.length === 0) {
138
- return Effect.fail(
139
- ContextInvalidInput.make({
140
- message: "Cannot summarize empty messages"
141
- })
142
- )
143
- }
144
-
145
- const summaryText = messages
146
- .slice(0, 8)
147
- .map((message) => `${message.role.toLowerCase()}: ${message.content.slice(0, 160)}`)
148
- .join("\n")
149
-
150
- return Effect.gen(function* () {
151
- const crypto = yield* Crypto.Crypto
152
- const id = yield* crypto.randomUUIDv4.pipe(
153
- Effect.mapError((error) =>
154
- ContextSummarizationFailed.make({
155
- message: `Failed to generate summary id: ${error.message}`
156
- })
157
- )
158
- )
159
- const timestamp = yield* DateTime.now
160
-
161
- return ConversationMessage.make({
162
- id,
163
- role: "Assistant",
164
- content: `Summary of previous context:\n${summaryText}`,
165
- timestamp,
166
- tokens: Math.max(1, targetTokens),
167
- metadata: { summary: "true" },
168
- important: true
169
- })
170
- })
171
- }
172
- }
173
-
174
- const annotateTokens = (
175
- messages: ReadonlyArray<ConversationMessage>,
176
- provider: LlmProvider,
177
- tokenCounter: TokenCounter
178
- ): ReadonlyArray<ConversationMessage> =>
179
- messages.map((message) =>
180
- message.tokens > 0
181
- ? message
182
- : ConversationMessage.make({
183
- ...message,
184
- tokens: tokenCounter.countMessage(provider, message)
185
- })
186
- )
187
-
188
- const dropOldest = (
189
- messages: ReadonlyArray<ConversationMessage>,
190
- maxTokens: number
191
- ): ReadonlyArray<ConversationMessage> => {
192
- let used = 0
193
- let kept: ReadonlyArray<ConversationMessage> = []
194
-
195
- for (const message of Array.from(messages).reverse()) {
196
- if (used + message.tokens <= maxTokens) {
197
- used += message.tokens
198
- kept = [message, ...kept]
199
- }
200
- }
201
-
202
- return kept
203
- }
204
-
205
- const slidingWindow = (
206
- messages: ReadonlyArray<ConversationMessage>,
207
- maxTokens: number
208
- ): ReadonlyArray<ConversationMessage> => {
209
- const system = messages.filter((message) => message.role === "System")
210
- const recentNonSystem = messages.filter((message) => message.role !== "System")
211
- const systemTokens = system.reduce((total, message) => total + message.tokens, 0)
212
- const tail = dropOldest(recentNonSystem, Math.max(0, maxTokens - systemTokens))
213
- return [...system, ...tail].slice(-messages.length)
214
- }
215
-
216
- const priorityWindow = (
217
- messages: ReadonlyArray<ConversationMessage>,
218
- maxTokens: number
219
- ): ReadonlyArray<ConversationMessage> => {
220
- const scored = messages.map((message, index) => ({
221
- message,
222
- score:
223
- (message.role === "System" ? 1_000 : 0) +
224
- (message.role === "Tool" ? 600 : 0) +
225
- (message.important ? 400 : 0) +
226
- index
227
- }))
228
- scored.sort(
229
- (left, right) =>
230
- right.score - left.score ||
231
- DateTime.toEpochMillis(left.message.timestamp) -
232
- DateTime.toEpochMillis(right.message.timestamp)
233
- )
234
-
235
- let used = 0
236
- const selected: Array<ConversationMessage> = []
237
- for (const candidate of scored) {
238
- if (used + candidate.message.tokens <= maxTokens) {
239
- used += candidate.message.tokens
240
- selected.push(candidate.message)
241
- }
242
- }
243
-
244
- return selected.sort(
245
- (left, right) =>
246
- DateTime.toEpochMillis(left.timestamp) - DateTime.toEpochMillis(right.timestamp)
247
- )
248
- }
249
-
250
- const summarizeOldMessages = <R>(
251
- messages: ReadonlyArray<ConversationMessage>,
252
- maxTokens: number,
253
- summaryTargetTokens: number,
254
- provider: LlmProvider,
255
- tokenCounter: TokenCounter,
256
- summarizer: MessageSummarizer<R>
257
- ): Effect.Effect<ReadonlyArray<ConversationMessage>, ContextError, R> => {
258
- if (messages.length <= 2) {
259
- return Effect.succeed(dropOldest(messages, maxTokens))
260
- }
261
-
262
- const splitIndex = Math.max(1, Math.floor(messages.length / 2))
263
- const oldMessages = messages.slice(0, splitIndex)
264
- const recentMessages = messages.slice(splitIndex)
265
-
266
- return Effect.map(summarizer.summarize(oldMessages, summaryTargetTokens), (summary) => {
267
- const annotatedSummary = ConversationMessage.make({
268
- ...summary,
269
- tokens: tokenCounter.countMessage(provider, summary)
270
- })
271
- return dropOldest([annotatedSummary, ...recentMessages], maxTokens)
272
- })
273
- }
274
-
275
- export function applyContextWindow(
276
- messages: ReadonlyArray<ConversationMessage>,
277
- provider: LlmProvider,
278
- limits: ContextLimits,
279
- strategy: DropOldestFifo | SlidingWindow | PriorityBased,
280
- tokenCounter?: TokenCounter
281
- ): Effect.Effect<ContextWindow, ContextError>
282
- export function applyContextWindow(
283
- messages: ReadonlyArray<ConversationMessage>,
284
- provider: LlmProvider,
285
- limits: ContextLimits,
286
- strategy: ContextTrimmingStrategy,
287
- tokenCounter?: TokenCounter
288
- ): Effect.Effect<ContextWindow, ContextError, Crypto.Crypto>
289
- export function applyContextWindow<R>(
290
- messages: ReadonlyArray<ConversationMessage>,
291
- provider: LlmProvider,
292
- limits: ContextLimits,
293
- strategy: ContextTrimmingStrategy,
294
- tokenCounter: TokenCounter,
295
- summarizer: MessageSummarizer<R>
296
- ): Effect.Effect<ContextWindow, ContextError, R>
297
- export function applyContextWindow<R>(
298
- messages: ReadonlyArray<ConversationMessage>,
299
- provider: LlmProvider,
300
- limits: ContextLimits,
301
- strategy: ContextTrimmingStrategy,
302
- tokenCounter: TokenCounter = defaultTokenCounter,
303
- summarizer?: MessageSummarizer<R>
304
- ): Effect.Effect<ContextWindow, ContextError, R | Crypto.Crypto> {
305
- if (limits.maxTokens <= 0) {
306
- return Effect.fail(
307
- ContextInvalidInput.make({
308
- message: "maxTokens must be > 0"
309
- })
310
- )
311
- }
312
-
313
- const withTokens = annotateTokens(messages, provider, tokenCounter)
314
- const totalBefore = withTokens.reduce((total, message) => total + message.tokens, 0)
315
-
316
- const trimmed: Effect.Effect<
317
- readonly [messages: ReadonlyArray<ConversationMessage>, wasTrimmed: boolean],
318
- ContextError,
319
- R | Crypto.Crypto
320
- > =
321
- totalBefore <= limits.maxTokens
322
- ? Effect.succeed([withTokens, false])
323
- : (() => {
324
- switch (strategy._tag) {
325
- case "DropOldestFifo":
326
- return Effect.succeed([dropOldest(withTokens, limits.maxTokens), true])
327
- case "SlidingWindow":
328
- return Effect.succeed([slidingWindow(withTokens, limits.maxTokens), true])
329
- case "PriorityBased":
330
- return Effect.succeed([priorityWindow(withTokens, limits.maxTokens), true])
331
- case "SummarizeOldMessages": {
332
- const summarized: Effect.Effect<
333
- ReadonlyArray<ConversationMessage>,
334
- ContextError,
335
- R | Crypto.Crypto
336
- > = summarizer === undefined
337
- ? summarizeOldMessages(
338
- withTokens,
339
- limits.maxTokens,
340
- strategy.summaryTargetTokens,
341
- provider,
342
- tokenCounter,
343
- simpleMessageSummarizer
344
- )
345
- : summarizeOldMessages(
346
- withTokens,
347
- limits.maxTokens,
348
- strategy.summaryTargetTokens,
349
- provider,
350
- tokenCounter,
351
- summarizer
352
- )
353
- return Effect.map(summarized, (messages) => [messages, true])
354
- }
355
- }
356
- })()
357
-
358
- return Effect.map(trimmed, ([finalMessages, wasTrimmed]) => {
359
- const totalTokens = finalMessages.reduce((total, message) => total + message.tokens, 0)
360
- return ContextWindow.make({
361
- messages: finalMessages,
362
- totalTokens,
363
- approachingLimit: totalTokens >= limits.maxTokens * limits.warningThresholdPct,
364
- trimmed: wasTrimmed
365
- })
366
- })
367
- }
@@ -1,21 +0,0 @@
1
- import * as Effect from "effect/Effect"
2
- import * as Schema from "effect/Schema"
3
-
4
- export const PromptRole = Schema.Literals(["System", "User", "Assistant", "Tool"])
5
- export type PromptRole = typeof PromptRole.Type
6
-
7
- const emptyMetadata: Readonly<Record<string, string>> = Object.freeze({})
8
-
9
- export class ConversationMessage extends Schema.Class<ConversationMessage>("ConversationMessage")({
10
- id: Schema.String,
11
- role: PromptRole,
12
- content: Schema.String,
13
- timestamp: Schema.DateTimeUtc,
14
- tokens: Schema.Int.pipe(Schema.withConstructorDefault(Effect.succeed(0))),
15
- model: Schema.optionalKey(Schema.String),
16
- costUsd: Schema.optionalKey(Schema.Number),
17
- metadata: Schema.Record(Schema.String, Schema.String).pipe(
18
- Schema.withConstructorDefault(Effect.succeed(emptyMetadata))
19
- ),
20
- important: Schema.Boolean.pipe(Schema.withConstructorDefault(Effect.succeed(false)))
21
- }) {}