@owlmeans/agent 0.1.18-rc.47 → 0.1.18-rc.49
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/README.md +4 -4
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/agent/SKILL.md +9 -9
- package/build/helpers/compaction/types.d.ts +49 -0
- package/build/helpers/compaction/types.d.ts.map +1 -0
- package/build/helpers/compaction/types.js.map +1 -0
- package/build/helpers/compaction.d.ts +6 -20
- package/build/helpers/compaction.d.ts.map +1 -1
- package/build/helpers/compaction.js +72 -81
- package/build/helpers/compaction.js.map +1 -1
- package/build/helpers/index.d.ts +3 -1
- package/build/helpers/index.d.ts.map +1 -1
- package/build/helpers/index.js.map +1 -1
- package/build/helpers/rolling/types.d.ts +16 -0
- package/build/helpers/rolling/types.d.ts.map +1 -0
- package/build/helpers/rolling/types.js +2 -0
- package/build/helpers/rolling/types.js.map +1 -0
- package/build/helpers/rolling.d.ts +1 -1
- package/build/helpers/rolling.d.ts.map +1 -1
- package/build/helpers/tools/types.d.ts +38 -0
- package/build/helpers/tools/types.d.ts.map +1 -0
- package/build/helpers/tools/types.js +2 -0
- package/build/helpers/{types.js.map → tools/types.js.map} +1 -1
- package/build/helpers/tools.d.ts +6 -26
- package/build/helpers/tools.d.ts.map +1 -1
- package/build/helpers/tools.js +27 -42
- package/build/helpers/tools.js.map +1 -1
- package/build/model.d.ts.map +1 -1
- package/build/model.js +4 -4
- package/build/model.js.map +1 -1
- package/build/pipeline/index.d.ts +2 -1
- package/build/pipeline/index.d.ts.map +1 -1
- package/build/pipeline/results/types.d.ts +139 -0
- package/build/pipeline/results/types.d.ts.map +1 -0
- package/build/pipeline/results/types.js +2 -0
- package/build/pipeline/results/types.js.map +1 -0
- package/build/pipeline/{types.local.d.ts → results/types.local.d.ts} +0 -18
- package/build/pipeline/results/types.local.d.ts.map +1 -0
- package/build/pipeline/results/types.local.js.map +1 -0
- package/build/pipeline/results.d.ts +2 -1
- package/build/pipeline/results.d.ts.map +1 -1
- package/build/pipeline/results.js +22 -22
- package/build/pipeline/results.js.map +1 -1
- package/build/pipeline/runner/types.d.ts +293 -0
- package/build/pipeline/runner/types.d.ts.map +1 -0
- package/build/pipeline/runner/types.js +2 -0
- package/build/pipeline/runner/types.js.map +1 -0
- package/build/pipeline/runner/types.local.d.ts +19 -0
- package/build/pipeline/runner/types.local.d.ts.map +1 -0
- package/build/pipeline/runner/types.local.js +2 -0
- package/build/pipeline/runner/types.local.js.map +1 -0
- package/build/pipeline/runner.d.ts +1 -1
- package/build/pipeline/runner.d.ts.map +1 -1
- package/build/pipeline/runner.js +10 -9
- package/build/pipeline/runner.js.map +1 -1
- package/build/pipeline/types.d.ts +2 -93
- package/build/pipeline/types.d.ts.map +1 -1
- package/build/plugins/export.d.ts +2 -2
- package/build/plugins/export.d.ts.map +1 -1
- package/build/plugins/export.js +2 -2
- package/build/plugins/export.js.map +1 -1
- package/build/plugins/inquiry.d.ts.map +1 -1
- package/build/plugins/inquiry.js +4 -4
- package/build/plugins/inquiry.js.map +1 -1
- package/build/plugins/memory-events.d.ts +3 -1
- package/build/plugins/memory-events.d.ts.map +1 -1
- package/build/plugins/memory-events.js +4 -2
- package/build/plugins/memory-events.js.map +1 -1
- package/build/plugins/memory-graph.d.ts +5 -1
- package/build/plugins/memory-graph.d.ts.map +1 -1
- package/build/plugins/memory-graph.js +4 -2
- package/build/plugins/memory-graph.js.map +1 -1
- package/build/plugins/summarize.d.ts.map +1 -1
- package/build/plugins/summarize.js +2 -2
- package/build/plugins/summarize.js.map +1 -1
- package/build/types.d.ts +5 -3
- package/build/types.d.ts.map +1 -1
- package/package.json +9 -9
- package/src/helpers/compaction/types.ts +51 -0
- package/src/helpers/compaction.ts +98 -101
- package/src/helpers/index.ts +3 -1
- package/src/helpers/rolling/types.ts +16 -0
- package/src/helpers/rolling.ts +1 -1
- package/src/helpers/tools/types.ts +37 -0
- package/src/helpers/tools.ts +39 -47
- package/src/model.ts +4 -4
- package/src/pipeline/index.ts +2 -1
- package/src/pipeline/{types.local.ts → results/types.local.ts} +0 -21
- package/src/pipeline/results/types.ts +152 -0
- package/src/pipeline/results.ts +25 -24
- package/src/pipeline/runner/types.local.ts +21 -0
- package/src/pipeline/{types.ts → runner/types.ts} +4 -156
- package/src/pipeline/runner.ts +14 -12
- package/src/plugins/export.ts +2 -2
- package/src/plugins/inquiry.ts +4 -4
- package/src/plugins/memory-events.ts +8 -2
- package/src/plugins/memory-graph.ts +8 -2
- package/src/plugins/summarize.ts +2 -2
- package/src/types.ts +4 -4
- package/tests/inquiry.spec.ts +14 -15
- package/tests/pipeline-inquiry.spec.ts +3 -3
- package/tests/plugins.spec.ts +10 -10
- package/tests/summary.spec.ts +8 -8
- package/tests/tools.spec.ts +11 -11
- package/build/helpers/types.d.ts +0 -42
- package/build/helpers/types.d.ts.map +0 -1
- package/build/pipeline/types.local.d.ts.map +0 -1
- package/build/pipeline/types.local.js.map +0 -1
- package/src/helpers/types.ts +0 -43
- /package/build/helpers/{types.js → compaction/types.js} +0 -0
- /package/build/pipeline/{types.local.js → results/types.local.js} +0 -0
|
@@ -19,7 +19,7 @@ import type { MemoryGraphApi, MemoryGraphOptions } from './types.js'
|
|
|
19
19
|
* make every write a potential act of forgetting, which is not a decision a single caller has the
|
|
20
20
|
* standing to take.
|
|
21
21
|
*/
|
|
22
|
-
export const
|
|
22
|
+
export const makeMemoryGraphApi = (
|
|
23
23
|
store: MemoryGraphStore,
|
|
24
24
|
options: Pick<MemoryGraphOptions, 'model' | 'maxNodeChars' | 'action'> & { run?: AgentRun } = {},
|
|
25
25
|
): MemoryGraphApi => {
|
|
@@ -96,7 +96,7 @@ export const memoryGraphPlugin = (options: MemoryGraphOptions = {}): AgentPlugin
|
|
|
96
96
|
|
|
97
97
|
const api = (run: AgentRun): MemoryGraphApi | null => store == null
|
|
98
98
|
? null
|
|
99
|
-
:
|
|
99
|
+
: makeMemoryGraphApi(store, { ...options, run })
|
|
100
100
|
|
|
101
101
|
return {
|
|
102
102
|
alias: MEMORY_GRAPH_PLUGIN,
|
|
@@ -188,3 +188,9 @@ export const memoryGraphPlugin = (options: MemoryGraphOptions = {}): AgentPlugin
|
|
|
188
188
|
},
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
|
+
|
|
192
|
+
/** @deprecated compat:factory-refactor — use `makeMemoryGraphApi(…)` */
|
|
193
|
+
export const memoryGraph = (
|
|
194
|
+
store: MemoryGraphStore,
|
|
195
|
+
options: Pick<MemoryGraphOptions, 'model' | 'maxNodeChars' | 'action'> & { run?: AgentRun } = {},
|
|
196
|
+
): MemoryGraphApi => makeMemoryGraphApi(store, options)
|
package/src/plugins/summarize.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AgentRunStatus, DEFAULT_ADVICE_CHARS, DEFAULT_EVENT_WINDOW, DEFAULT_SUMMARY_CHARS, truncateAt,
|
|
3
3
|
} from '@owlmeans/agent-common'
|
|
4
|
-
import { composeCompaction } from '../helpers/compaction.js'
|
|
5
4
|
import type { AgentPlugin } from '../types.js'
|
|
6
5
|
import { ADVICE_HEADING, HISTORY_HEADING } from './consts.local.js'
|
|
7
6
|
import { SUMMARIZE_PLUGIN } from './consts.js'
|
|
8
7
|
import type { SummarizeOptions } from './types.js'
|
|
8
|
+
import { compactionHelper } from '../helpers/compaction.js'
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -73,7 +73,7 @@ export const summarizePlugin = (options: SummarizeOptions = {}): AgentPlugin =>
|
|
|
73
73
|
return
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
const compaction = await composeCompaction({
|
|
76
|
+
const compaction = await compactionHelper.composeCompaction({
|
|
77
77
|
model: model?.(run),
|
|
78
78
|
prompt: run.prompt,
|
|
79
79
|
messages: result?.messages ?? [],
|
package/src/types.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type { AgentRunStatus, ConversationEvent, ConversationRef, PipelineSpec,
|
|
|
8
8
|
import type { BaseCheckpointSaver } from '@langchain/langgraph'
|
|
9
9
|
import type { AgentTransport } from './runtime/types.js'
|
|
10
10
|
import type { ConversationStore, PipelineRunStore } from './stores/types.js'
|
|
11
|
-
import type { PipelineModel, PipelineOptions } from './pipeline/types.js'
|
|
11
|
+
import type { PipelineModel, PipelineOptions } from './pipeline/runner/types.js'
|
|
12
12
|
|
|
13
13
|
/** Tools an agent may call, keyed however the caller likes — resolution is by `tool.name`. */
|
|
14
14
|
export interface AgentToolSet { [key: string]: StructuredToolInterface }
|
|
@@ -185,8 +185,8 @@ export interface WithAgentsService {
|
|
|
185
185
|
agents: () => AgentService
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
export
|
|
188
|
+
export interface AgentContext<C extends BasicConfig = BasicConfig> extends BasicContext<C>, WithAgentsService {}
|
|
189
189
|
|
|
190
|
-
export
|
|
190
|
+
export interface AgentServiceApi extends Pick<
|
|
191
191
|
AgentService, 'agent' | 'use' | 'plugins' | 'flow' | 'transport' | 'conversation' | 'pipeline'
|
|
192
|
-
>
|
|
192
|
+
> {}
|
package/tests/inquiry.spec.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test'
|
|
2
|
-
import {
|
|
3
|
-
import { InquiryDeclined, InquiryUnavailable } from '@owlmeans/llm'
|
|
2
|
+
import { InquiryDeclined, InquiryUnavailable, retryHelper } from '@owlmeans/llm'
|
|
4
3
|
import { InquiryKind } from '@owlmeans/llm-common'
|
|
5
4
|
import type { Inquiry, InquiryAnswer } from '@owlmeans/llm-common'
|
|
6
|
-
import { ASK_USER_TOOL, inquiryPlugin,
|
|
5
|
+
import { ASK_USER_TOOL, inquiryPlugin, toolHelper } from '../src/index.js'
|
|
7
6
|
import type { AgentRun, AgentToolSet } from '../src/index.js'
|
|
8
7
|
|
|
9
8
|
/**
|
|
@@ -43,7 +42,7 @@ describe('agent — the inquiry plugin', () => {
|
|
|
43
42
|
test('an answer comes back as JSON', async () => {
|
|
44
43
|
const tools = toolsOf(async inquiry => ({ inquiryId: inquiry.id, value: 'a' }))
|
|
45
44
|
|
|
46
|
-
const result = await safeInvokeTool(tools, callOf(asking))
|
|
45
|
+
const result = await toolHelper.safeInvokeTool(tools, callOf(asking))
|
|
47
46
|
|
|
48
47
|
expect(JSON.parse(result as string).value).toBe('a')
|
|
49
48
|
})
|
|
@@ -56,7 +55,7 @@ describe('agent — the inquiry plugin', () => {
|
|
|
56
55
|
return { inquiryId: inquiry.id, value: 'x' }
|
|
57
56
|
})
|
|
58
57
|
|
|
59
|
-
await safeInvokeTool(tools, callOf({
|
|
58
|
+
await toolHelper.safeInvokeTool(tools, callOf({
|
|
60
59
|
question: 'Which one?', kind: InquiryKind.Choice, context: 'two candidates',
|
|
61
60
|
options: [{ value: 'a', label: 'A' }, { value: 'b', label: 'B' }],
|
|
62
61
|
}))
|
|
@@ -75,28 +74,28 @@ describe('agent — the inquiry plugin', () => {
|
|
|
75
74
|
return { inquiryId: inquiry.id, value: 'a' }
|
|
76
75
|
})
|
|
77
76
|
|
|
78
|
-
const result = await safeInvokeTool(tools, callOf({
|
|
77
|
+
const result = await toolHelper.safeInvokeTool(tools, callOf({
|
|
79
78
|
question: 'Which one?', kind: InquiryKind.Choice, options: [{ value: 'a', label: 'A' }],
|
|
80
79
|
}))
|
|
81
80
|
|
|
82
|
-
expect(isToolError(result)).toBe(true)
|
|
81
|
+
expect(toolHelper.isToolError(result)).toBe(true)
|
|
83
82
|
expect((result as { error: string }).error).toContain('between 2 and')
|
|
84
83
|
expect(reached).toBe(false)
|
|
85
84
|
})
|
|
86
85
|
|
|
87
86
|
test('tells the model to decide for itself when nobody is there', async () => {
|
|
88
|
-
const result = await safeInvokeTool(toolsOf(async () => null), callOf(asking))
|
|
87
|
+
const result = await toolHelper.safeInvokeTool(toolsOf(async () => null), callOf(asking))
|
|
89
88
|
|
|
90
|
-
expect(isToolError(result)).toBe(true)
|
|
89
|
+
expect(toolHelper.isToolError(result)).toBe(true)
|
|
91
90
|
expect((result as { error: string }).error).toContain('Decide yourself')
|
|
92
91
|
})
|
|
93
92
|
|
|
94
93
|
test('reads a decline as an answer the model must act on, not as a failure', async () => {
|
|
95
94
|
const tools = toolsOf(async () => { throw new InquiryDeclined('q1') })
|
|
96
95
|
|
|
97
|
-
const result = await safeInvokeTool(tools, callOf(asking))
|
|
96
|
+
const result = await toolHelper.safeInvokeTool(tools, callOf(asking))
|
|
98
97
|
|
|
99
|
-
expect(isToolError(result)).toBe(true)
|
|
98
|
+
expect(toolHelper.isToolError(result)).toBe(true)
|
|
100
99
|
expect((result as { error: string }).error).toContain('declined')
|
|
101
100
|
})
|
|
102
101
|
|
|
@@ -105,17 +104,17 @@ describe('agent — the inquiry plugin', () => {
|
|
|
105
104
|
// would cost the agent all 64 turns on a channel that will never answer.
|
|
106
105
|
const tools = toolsOf(async () => { throw new InquiryUnavailable('connector') })
|
|
107
106
|
|
|
108
|
-
expect(isFatalError(new InquiryUnavailable('connector'))).not.toBeNull()
|
|
109
|
-
await expect(safeInvokeTool(tools, callOf(asking), e => isFatalError(e) != null))
|
|
107
|
+
expect(retryHelper.isFatalError(new InquiryUnavailable('connector'))).not.toBeNull()
|
|
108
|
+
await expect(toolHelper.safeInvokeTool(tools, callOf(asking), e => retryHelper.isFatalError(e) != null))
|
|
110
109
|
.rejects.toThrow(/unavailable/)
|
|
111
110
|
})
|
|
112
111
|
|
|
113
112
|
test('contains any other channel failure as a tool error', async () => {
|
|
114
113
|
const tools = toolsOf(async () => { throw new Error('the socket died') })
|
|
115
114
|
|
|
116
|
-
const result = await safeInvokeTool(tools, callOf(asking), e => isFatalError(e) != null)
|
|
115
|
+
const result = await toolHelper.safeInvokeTool(tools, callOf(asking), e => retryHelper.isFatalError(e) != null)
|
|
117
116
|
|
|
118
|
-
expect(isToolError(result)).toBe(true)
|
|
117
|
+
expect(toolHelper.isToolError(result)).toBe(true)
|
|
119
118
|
expect((result as { error: string }).error).toContain('the socket died')
|
|
120
119
|
})
|
|
121
120
|
})
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
INQUIRY_ANSWERS_KEY, PipelineNotResumableError, PipelineRunStatus,
|
|
4
4
|
} from '@owlmeans/agent-common'
|
|
5
5
|
import type { PipelineSpec } from '@owlmeans/agent-common'
|
|
6
|
-
import { INQUIRY_STATE_TEXT_CHARS, InquiryKind,
|
|
6
|
+
import { INQUIRY_STATE_TEXT_CHARS, InquiryKind, inquiryHelper } from '@owlmeans/llm-common'
|
|
7
7
|
import type { Inquiry, InquiryAnswer } from '@owlmeans/llm-common'
|
|
8
8
|
import { createMemoryPipelineRunStore, makePipeline } from '../src/index.js'
|
|
9
9
|
import type { PipelineModel, PipelineRunStore } from '../src/index.js'
|
|
@@ -61,7 +61,7 @@ const build = (
|
|
|
61
61
|
for (const asked of questions) {
|
|
62
62
|
const answer = await ctx.ask(asked)
|
|
63
63
|
ctx.deps.got.push(answer)
|
|
64
|
-
picked.push(answeredWith(answer) ?? '')
|
|
64
|
+
picked.push(inquiryHelper.answeredWith(answer) ?? '')
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
return { picked: picked.join('+') }
|
|
@@ -143,7 +143,7 @@ describe('agent — a pipeline that asks', () => {
|
|
|
143
143
|
|
|
144
144
|
expect(finished.status).toBe(PipelineRunStatus.Done)
|
|
145
145
|
// `q1` was answered live before the run parked; a resume carrying only `q2` must not cost it.
|
|
146
|
-
expect(answeredWith(ran.got[0])).toBe('one')
|
|
146
|
+
expect(inquiryHelper.answeredWith(ran.got[0])).toBe('one')
|
|
147
147
|
// What the state holds is what a resumed step reads back — there is no fuller copy anywhere.
|
|
148
148
|
expect(ran.got[1].text).toHaveLength(INQUIRY_STATE_TEXT_CHARS)
|
|
149
149
|
expect(answersOf(finished.state).q2.text).toHaveLength(INQUIRY_STATE_TEXT_CHARS)
|
package/tests/plugins.spec.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { makePromptService } from '@owlmeans/llm'
|
|
|
5
5
|
import { AgentRunStatus } from '@owlmeans/agent-common'
|
|
6
6
|
import {
|
|
7
7
|
createMemoryConversationStore, createMemoryEventStore, createMemoryGraphStore, makeAgentModel,
|
|
8
|
-
|
|
8
|
+
makeMemoryEventsApi, makeMemoryGraphApi, memoryEventsPlugin, memoryGraphPlugin, summarizePlugin,
|
|
9
9
|
} from '../src/index.js'
|
|
10
10
|
import { scriptedModel } from './_tools/model.js'
|
|
11
11
|
|
|
@@ -125,7 +125,7 @@ describe('agent — the summarize plugin', () => {
|
|
|
125
125
|
describe('agent — the subsystem memory graph', () => {
|
|
126
126
|
test('merges into a node instead of replacing it', async () => {
|
|
127
127
|
// Replacing would make every write a potential act of forgetting.
|
|
128
|
-
const graph =
|
|
128
|
+
const graph = makeMemoryGraphApi(createMemoryGraphStore())
|
|
129
129
|
await graph.write('p1', 'auth', 'uses OIDC')
|
|
130
130
|
await graph.write('p1', 'auth', 'tokens live 1h')
|
|
131
131
|
|
|
@@ -135,7 +135,7 @@ describe('agent — the subsystem memory graph', () => {
|
|
|
135
135
|
})
|
|
136
136
|
|
|
137
137
|
test('compacts a node that outgrows its budget', async () => {
|
|
138
|
-
const graph =
|
|
138
|
+
const graph = makeMemoryGraphApi(createMemoryGraphStore(), {
|
|
139
139
|
maxNodeChars: 60,
|
|
140
140
|
model: () => answering('the folded account'),
|
|
141
141
|
run: {} as never,
|
|
@@ -149,7 +149,7 @@ describe('agent — the subsystem memory graph', () => {
|
|
|
149
149
|
})
|
|
150
150
|
|
|
151
151
|
test('accumulates links and never links a node to itself', async () => {
|
|
152
|
-
const graph =
|
|
152
|
+
const graph = makeMemoryGraphApi(createMemoryGraphStore())
|
|
153
153
|
await graph.write('p1', 'auth', 'x', ['db'])
|
|
154
154
|
await graph.write('p1', 'auth', 'y', ['api', 'auth'])
|
|
155
155
|
|
|
@@ -158,7 +158,7 @@ describe('agent — the subsystem memory graph', () => {
|
|
|
158
158
|
})
|
|
159
159
|
|
|
160
160
|
test('follows links to the requested depth and no further', async () => {
|
|
161
|
-
const graph =
|
|
161
|
+
const graph = makeMemoryGraphApi(createMemoryGraphStore())
|
|
162
162
|
await graph.write('p1', 'auth', 'a', ['db'])
|
|
163
163
|
await graph.write('p1', 'db', 'b', ['storage'])
|
|
164
164
|
await graph.write('p1', 'storage', 'c')
|
|
@@ -170,7 +170,7 @@ describe('agent — the subsystem memory graph', () => {
|
|
|
170
170
|
})
|
|
171
171
|
|
|
172
172
|
test('survives a cycle in the graph', async () => {
|
|
173
|
-
const graph =
|
|
173
|
+
const graph = makeMemoryGraphApi(createMemoryGraphStore())
|
|
174
174
|
await graph.write('p1', 'a', 'x', ['b'])
|
|
175
175
|
await graph.write('p1', 'b', 'y', ['a'])
|
|
176
176
|
|
|
@@ -181,7 +181,7 @@ describe('agent — the subsystem memory graph', () => {
|
|
|
181
181
|
// Bulk-injecting notes spends the context window on knowledge the run cannot tell apart from
|
|
182
182
|
// what it needs; the agent pulls what it wants by name.
|
|
183
183
|
const store = createMemoryGraphStore()
|
|
184
|
-
await
|
|
184
|
+
await makeMemoryGraphApi(store).write('p1', 'auth', 'a long private note', ['db'])
|
|
185
185
|
|
|
186
186
|
const scripted = scriptedModel([{ content: 'ok' }])
|
|
187
187
|
await makeAgentModel({
|
|
@@ -212,7 +212,7 @@ describe('agent — the subsystem memory graph', () => {
|
|
|
212
212
|
|
|
213
213
|
describe('agent — the event sequence memory', () => {
|
|
214
214
|
test('keeps only the most recent entries', async () => {
|
|
215
|
-
const events =
|
|
215
|
+
const events = makeMemoryEventsApi(createMemoryEventStore(), { limit: 2 })
|
|
216
216
|
for (const content of ['a', 'b', 'c']) {
|
|
217
217
|
await events.append('p1', 'note', content)
|
|
218
218
|
}
|
|
@@ -221,7 +221,7 @@ describe('agent — the event sequence memory', () => {
|
|
|
221
221
|
})
|
|
222
222
|
|
|
223
223
|
test('caps a single entry — an event is a line, not a document', async () => {
|
|
224
|
-
const events =
|
|
224
|
+
const events = makeMemoryEventsApi(createMemoryEventStore(), { maxEventChars: 20 })
|
|
225
225
|
await events.append('p1', 'note', 'x'.repeat(500))
|
|
226
226
|
|
|
227
227
|
expect((await events.read('p1', 1))[0].content.length).toBeLessThanOrEqual(20)
|
|
@@ -229,7 +229,7 @@ describe('agent — the event sequence memory', () => {
|
|
|
229
229
|
|
|
230
230
|
test('injects the recent window into a run', async () => {
|
|
231
231
|
const store = createMemoryEventStore()
|
|
232
|
-
await
|
|
232
|
+
await makeMemoryEventsApi(store).append('p1', 'deploy', 'shipped version 3')
|
|
233
233
|
|
|
234
234
|
const scripted = scriptedModel([{ content: 'ok' }])
|
|
235
235
|
await makeAgentModel({
|
package/tests/summary.spec.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { describe, expect, test } from 'bun:test'
|
|
|
2
2
|
import { AIMessage, HumanMessage, ToolMessage } from '@langchain/core/messages'
|
|
3
3
|
import type { LlmModel } from '@owlmeans/llm'
|
|
4
4
|
import { AgentRunStatus } from '@owlmeans/agent-common'
|
|
5
|
-
import {
|
|
5
|
+
import { composeRollingSummary, compactionHelper } from '../src/index.js'
|
|
6
6
|
|
|
7
7
|
/** A model double: the helpers only ever call `invoke` (structured) or `ask` (text). */
|
|
8
8
|
const answering = (answer: unknown): LlmModel => ({
|
|
@@ -24,7 +24,7 @@ const conversation = [
|
|
|
24
24
|
|
|
25
25
|
describe('agent — conversation compaction', () => {
|
|
26
26
|
test('returns both parts, each inside its own cap', async () => {
|
|
27
|
-
const result = await composeCompaction({
|
|
27
|
+
const result = await compactionHelper.composeCompaction({
|
|
28
28
|
model: answering({ summary: 'S'.repeat(5_000), advice: 'A'.repeat(5_000) }),
|
|
29
29
|
prompt: 'rename the header',
|
|
30
30
|
messages: conversation,
|
|
@@ -41,7 +41,7 @@ describe('agent — conversation compaction', () => {
|
|
|
41
41
|
test('falls back to a usable event when the model fails', async () => {
|
|
42
42
|
// An exhausted budget is the common case, and asking again would fail the same way — so the
|
|
43
43
|
// fallback has to carry the facts the caller already holds rather than an apology.
|
|
44
|
-
const result = await composeCompaction({
|
|
44
|
+
const result = await compactionHelper.composeCompaction({
|
|
45
45
|
model: failing(),
|
|
46
46
|
prompt: 'rename the header',
|
|
47
47
|
messages: conversation,
|
|
@@ -55,7 +55,7 @@ describe('agent — conversation compaction', () => {
|
|
|
55
55
|
})
|
|
56
56
|
|
|
57
57
|
test('treats an empty summary as a non-answer, not a short one', async () => {
|
|
58
|
-
const result = await composeCompaction({
|
|
58
|
+
const result = await compactionHelper.composeCompaction({
|
|
59
59
|
model: answering({ summary: ' ', advice: 'do something' }),
|
|
60
60
|
prompt: 'rename the header',
|
|
61
61
|
messages: conversation,
|
|
@@ -66,7 +66,7 @@ describe('agent — conversation compaction', () => {
|
|
|
66
66
|
})
|
|
67
67
|
|
|
68
68
|
test('works with no model at all', async () => {
|
|
69
|
-
const result = await composeCompaction({
|
|
69
|
+
const result = await compactionHelper.composeCompaction({
|
|
70
70
|
prompt: 'rename the header', messages: conversation, status: AgentRunStatus.Ok,
|
|
71
71
|
})
|
|
72
72
|
|
|
@@ -74,7 +74,7 @@ describe('agent — conversation compaction', () => {
|
|
|
74
74
|
})
|
|
75
75
|
|
|
76
76
|
test('drops an empty advice rather than storing a blank field', async () => {
|
|
77
|
-
const result = await composeCompaction({
|
|
77
|
+
const result = await compactionHelper.composeCompaction({
|
|
78
78
|
model: answering({ summary: 'did the thing', advice: '' }),
|
|
79
79
|
prompt: 'p', messages: conversation, status: AgentRunStatus.Ok,
|
|
80
80
|
})
|
|
@@ -85,7 +85,7 @@ describe('agent — conversation compaction', () => {
|
|
|
85
85
|
|
|
86
86
|
describe('agent — transcript rendering', () => {
|
|
87
87
|
test('names the tools a message called when it carried no text', async () => {
|
|
88
|
-
expect(renderTranscript(conversation)).toContain('called write_file')
|
|
88
|
+
expect(compactionHelper.renderTranscript(conversation)).toContain('called write_file')
|
|
89
89
|
})
|
|
90
90
|
|
|
91
91
|
test('keeps the tail when the budget binds', () => {
|
|
@@ -95,7 +95,7 @@ describe('agent — transcript rendering', () => {
|
|
|
95
95
|
new AIMessage({ content: 'the final answer' }),
|
|
96
96
|
]
|
|
97
97
|
|
|
98
|
-
const rendered = renderTranscript(long, 100)
|
|
98
|
+
const rendered = compactionHelper.renderTranscript(long, 100)
|
|
99
99
|
|
|
100
100
|
expect(rendered).toContain('the final answer')
|
|
101
101
|
expect(rendered).not.toContain('AAAA')
|
package/tests/tools.spec.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test'
|
|
2
2
|
import { tool } from '@langchain/core/tools'
|
|
3
3
|
import * as z from 'zod'
|
|
4
|
-
import { isToolError, safeInvokeTool } from '../src/index.js'
|
|
5
4
|
import type { AgentToolSet } from '../src/index.js'
|
|
5
|
+
import { toolHelper } from '../src/index.js'
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* No model involved: these pin the containment contract the tool loop depends on, which is what
|
|
@@ -33,21 +33,21 @@ const callOf = (name: string, args: Record<string, unknown>) =>
|
|
|
33
33
|
|
|
34
34
|
describe('agent — tool invocation is contained', () => {
|
|
35
35
|
test('passes a valid call through to the tool', async () => {
|
|
36
|
-
expect(await safeInvokeTool(tools, callOf('get_structured_list', { type: 'ui-layout' })))
|
|
36
|
+
expect(await toolHelper.safeInvokeTool(tools, callOf('get_structured_list', { type: 'ui-layout' })))
|
|
37
37
|
.toBe('listed:ui-layout')
|
|
38
38
|
})
|
|
39
39
|
|
|
40
40
|
test('an out-of-enum argument resolves to an error naming the valid options', async () => {
|
|
41
|
-
const result = await safeInvokeTool(tools, callOf('get_structured_list', { type: 'ui-navigation' }))
|
|
41
|
+
const result = await toolHelper.safeInvokeTool(tools, callOf('get_structured_list', { type: 'ui-navigation' }))
|
|
42
42
|
|
|
43
|
-
expect(isToolError(result)).toBe(true)
|
|
43
|
+
expect(toolHelper.isToolError(result)).toBe(true)
|
|
44
44
|
expect((result as { error: string }).error).toContain('ui-screens')
|
|
45
45
|
})
|
|
46
46
|
|
|
47
47
|
test('a throwing tool resolves to an error instead of rejecting', async () => {
|
|
48
|
-
const result = await safeInvokeTool(tools, callOf('explodes', {}))
|
|
48
|
+
const result = await toolHelper.safeInvokeTool(tools, callOf('explodes', {}))
|
|
49
49
|
|
|
50
|
-
expect(isToolError(result)).toBe(true)
|
|
50
|
+
expect(toolHelper.isToolError(result)).toBe(true)
|
|
51
51
|
expect((result as { error: string }).error).toContain('the tool itself failed')
|
|
52
52
|
})
|
|
53
53
|
|
|
@@ -56,12 +56,12 @@ describe('agent — tool invocation is contained', () => {
|
|
|
56
56
|
// variable would otherwise lose the tool permanently.
|
|
57
57
|
const mismatched = { some_local_name: tools.get_structured_list } as unknown as AgentToolSet
|
|
58
58
|
|
|
59
|
-
expect(await safeInvokeTool(mismatched, callOf('get_structured_list', { type: 'ui-layout' })))
|
|
59
|
+
expect(await toolHelper.safeInvokeTool(mismatched, callOf('get_structured_list', { type: 'ui-layout' })))
|
|
60
60
|
.toBe('listed:ui-layout')
|
|
61
61
|
})
|
|
62
62
|
|
|
63
63
|
test('an unknown tool name resolves to an error instead of throwing', async () => {
|
|
64
|
-
const result = await safeInvokeTool(tools, callOf('hallucinated_tool', {}))
|
|
64
|
+
const result = await toolHelper.safeInvokeTool(tools, callOf('hallucinated_tool', {}))
|
|
65
65
|
|
|
66
66
|
expect((result as { error: string }).error).toContain('not found')
|
|
67
67
|
})
|
|
@@ -83,9 +83,9 @@ describe('agent — tool invocation is contained', () => {
|
|
|
83
83
|
} as unknown as AgentToolSet
|
|
84
84
|
const fatal = (e: unknown): boolean => e instanceof OutOfTokens
|
|
85
85
|
|
|
86
|
-
expect(safeInvokeTool(exploding, callOf('broke', {}), fatal)).rejects.toThrow('no balance')
|
|
86
|
+
expect(toolHelper.safeInvokeTool(exploding, callOf('broke', {}), fatal)).rejects.toThrow('no balance')
|
|
87
87
|
|
|
88
|
-
const contained = await safeInvokeTool(exploding, callOf('ordinary', {}), fatal)
|
|
89
|
-
expect(isToolError(contained)).toBe(true)
|
|
88
|
+
const contained = await toolHelper.safeInvokeTool(exploding, callOf('ordinary', {}), fatal)
|
|
89
|
+
expect(toolHelper.isToolError(contained)).toBe(true)
|
|
90
90
|
})
|
|
91
91
|
})
|
package/build/helpers/types.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { LlmModel } from '@owlmeans/llm';
|
|
2
|
-
import type { BaseMessage } from '@langchain/core/messages';
|
|
3
|
-
import { AgentRunStatus } from '@owlmeans/agent-common';
|
|
4
|
-
export interface Compaction {
|
|
5
|
-
summary: string;
|
|
6
|
-
advice?: string;
|
|
7
|
-
}
|
|
8
|
-
export interface CompactionInput {
|
|
9
|
-
/** Omit to skip the model entirely and take the deterministic path. */
|
|
10
|
-
model?: LlmModel;
|
|
11
|
-
/** The ask that opened the run. */
|
|
12
|
-
prompt: string;
|
|
13
|
-
messages: readonly BaseMessage[];
|
|
14
|
-
status: AgentRunStatus;
|
|
15
|
-
/** What happened after the loop — a validation verdict, a build result. */
|
|
16
|
-
note?: string;
|
|
17
|
-
maxSummaryChars?: number;
|
|
18
|
-
maxAdviceChars?: number;
|
|
19
|
-
/** LangChain `runName`. Give it a value the application filters, or the summary of a run streams into the user's view of that run. */
|
|
20
|
-
action?: string;
|
|
21
|
-
/** How much of the transcript to show the model. */
|
|
22
|
-
maxTranscriptChars?: number;
|
|
23
|
-
}
|
|
24
|
-
export interface RollingSummaryInput {
|
|
25
|
-
/** Omit to skip the model and take the deterministic path. */
|
|
26
|
-
model?: LlmModel;
|
|
27
|
-
/** The prose account so far. Empty on the first fold. */
|
|
28
|
-
previous: string;
|
|
29
|
-
/** What just happened, as one line. */
|
|
30
|
-
event: string;
|
|
31
|
-
/** Anything the fold may use but that need not survive into the summary. */
|
|
32
|
-
details?: string;
|
|
33
|
-
/** Hard ceiling on the returned prose, in characters. */
|
|
34
|
-
maxChars: number;
|
|
35
|
-
/** LangChain `runName`. Give it a value the application filters out of its user-facing stream. */
|
|
36
|
-
action?: string;
|
|
37
|
-
}
|
|
38
|
-
/** The shape a contained tool failure comes back as. Matches what tool bodies return themselves. */
|
|
39
|
-
export interface ToolErrorResponse {
|
|
40
|
-
error: string;
|
|
41
|
-
}
|
|
42
|
-
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/helpers/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAEvD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,KAAK,CAAC,EAAE,QAAQ,CAAA;IAChB,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAA;IAChC,MAAM,EAAE,cAAc,CAAA;IACtB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,sIAAsI;IACtI,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oDAAoD;IACpD,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,8DAA8D;IAC9D,KAAK,CAAC,EAAE,QAAQ,CAAA;IAChB,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAA;IAChB,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAA;IAChB,kGAAkG;IAClG,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,oGAAoG;AACpG,MAAM,WAAW,iBAAiB;IAAG,KAAK,EAAE,MAAM,CAAA;CAAE"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.local.d.ts","sourceRoot":"","sources":["../../src/pipeline/types.local.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAC3M,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AACjE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAE/D,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAClB,KAAK,EAAE,oBAAoB,EAAE,CAAA;CAC9B;AAED,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,qBAAqB,CAAA;IAC5B,IAAI,EAAE,cAAc,CAAA;IACpB,kFAAkF;IAClF,QAAQ,EAAE,OAAO,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,OAAO,EAAE,qBAAqB,CAAA;IAC9B,IAAI,EAAE,YAAY,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAA;IAC3C,QAAQ,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACvC,KAAK,EAAE,qBAAqB,EAAE,CAAA;IAC9B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IAC/B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IAC/B,IAAI,EAAE,CAAC,CAAA;CACR;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,oBAAoB,EAAE,CAAA;IAC7B,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,MAAM,EAAE,sBAAsB,CAAA;IAC9B,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,IAAI,CAAC,EAAE,eAAe,CAAA;IACtB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,CACP,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC9B,UAAU,CAAA;IACf,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,KAAK,UAAU,CAAA;IAC9D,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,mBAAmB,CAAA;KAAE,KAAK;QAC7D,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;KAC/E,CAAA;CACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.local.js","sourceRoot":"","sources":["../../src/pipeline/types.local.ts"],"names":[],"mappings":""}
|
package/src/helpers/types.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import type { LlmModel } from '@owlmeans/llm'
|
|
2
|
-
import type { BaseMessage } from '@langchain/core/messages'
|
|
3
|
-
import { AgentRunStatus } from '@owlmeans/agent-common'
|
|
4
|
-
|
|
5
|
-
export interface Compaction {
|
|
6
|
-
summary: string
|
|
7
|
-
advice?: string
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface CompactionInput {
|
|
11
|
-
/** Omit to skip the model entirely and take the deterministic path. */
|
|
12
|
-
model?: LlmModel
|
|
13
|
-
/** The ask that opened the run. */
|
|
14
|
-
prompt: string
|
|
15
|
-
messages: readonly BaseMessage[]
|
|
16
|
-
status: AgentRunStatus
|
|
17
|
-
/** What happened after the loop — a validation verdict, a build result. */
|
|
18
|
-
note?: string
|
|
19
|
-
maxSummaryChars?: number
|
|
20
|
-
maxAdviceChars?: number
|
|
21
|
-
/** LangChain `runName`. Give it a value the application filters, or the summary of a run streams into the user's view of that run. */
|
|
22
|
-
action?: string
|
|
23
|
-
/** How much of the transcript to show the model. */
|
|
24
|
-
maxTranscriptChars?: number
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface RollingSummaryInput {
|
|
28
|
-
/** Omit to skip the model and take the deterministic path. */
|
|
29
|
-
model?: LlmModel
|
|
30
|
-
/** The prose account so far. Empty on the first fold. */
|
|
31
|
-
previous: string
|
|
32
|
-
/** What just happened, as one line. */
|
|
33
|
-
event: string
|
|
34
|
-
/** Anything the fold may use but that need not survive into the summary. */
|
|
35
|
-
details?: string
|
|
36
|
-
/** Hard ceiling on the returned prose, in characters. */
|
|
37
|
-
maxChars: number
|
|
38
|
-
/** LangChain `runName`. Give it a value the application filters out of its user-facing stream. */
|
|
39
|
-
action?: string
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** The shape a contained tool failure comes back as. Matches what tool bodies return themselves. */
|
|
43
|
-
export interface ToolErrorResponse { error: string }
|
|
File without changes
|
|
File without changes
|