Skip to content

Commit 92e05b4

Browse files
committed
refactor: streamline agent runtime options and enhance turn context handling
1 parent bef31e0 commit 92e05b4

8 files changed

Lines changed: 98 additions & 113 deletions

File tree

agent/runtime/AgentModels.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

agent/runtime/AgentRuntime.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,21 @@ import { contextSchema, toLangchainAgentContext } from "./AgentContext.js";
1010

1111
export type AgentRuntimeOptions = {
1212
name: string;
13-
adminforth: IAdminForth;
14-
checkpointer: BaseCheckpointSaver;
13+
getAdminforth: () => IAdminForth;
14+
getCheckpointer: () => BaseCheckpointSaver;
1515
toolProvider: AgentToolProvider;
1616
};
1717

1818
export class AgentRuntime {
1919
constructor(private readonly options: AgentRuntimeOptions) {}
2020

2121
async stream(input: AgentRuntimeRunInput) {
22-
const tools = await this.options.toolProvider.getTools();
2322
const apiBasedTools = this.options.toolProvider.getApiBasedTools();
23+
const tools = await this.options.toolProvider.getTools(apiBasedTools);
24+
const adminforth = this.options.getAdminforth();
2425
const apiBasedToolsMiddleware = createApiBasedToolsMiddleware(
2526
apiBasedTools,
26-
this.options.adminforth,
27+
adminforth,
2728
);
2829
const sequenceDebugMiddleware = createSequenceDebugMiddleware(
2930
input.observability.sequenceDebugSink,
@@ -42,7 +43,7 @@ export class AgentRuntime {
4243
const agent = createAgent({
4344
name: this.options.name,
4445
model: input.models.model,
45-
checkpointer: this.options.checkpointer,
46+
checkpointer: this.options.getCheckpointer(),
4647
tools,
4748
contextSchema,
4849
middleware,
@@ -58,7 +59,7 @@ export class AgentRuntime {
5859
},
5960
context: toLangchainAgentContext({
6061
...input.context,
61-
adminBaseUrl: this.options.adminforth.config.baseUrlSlashed,
62+
adminBaseUrl: adminforth.config.baseUrlSlashed,
6263
emit: input.observability.emit,
6364
sequenceDebugSink: input.observability.sequenceDebugSink,
6465
}),

agent/runtime/AgentRuntimeFactory.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

agent/tools/AgentToolProvider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ export class AgentToolProvider {
1616
);
1717
}
1818

19-
async getTools() {
19+
async getTools(apiBasedTools: Record<string, ApiBasedTool>) {
2020
const adminforth = this.getAdminforth();
2121

2222
return createAgentTools(
2323
adminforth.config.customization.customComponentsDir ?? "custom",
24-
this.getApiBasedTools(),
24+
apiBasedTools,
2525
adminforth.activatedPlugins.map((plugin) => plugin.customFolderPath),
2626
);
2727
}

agent/turn/TurnContextBuilder.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type { AdminUser, IAdminForth } from "adminforth";
2+
import type { AgentTurnContext, BaseAgentTurnInput } from "./turnTypes.js";
3+
4+
export type UserContextProvider = {
5+
getUserTimeZone(adminUser: AdminUser): Promise<string | null | undefined> | string | null | undefined;
6+
};
7+
8+
export class TurnContextBuilder {
9+
constructor(
10+
private readonly getAdminforth: () => IAdminForth,
11+
private readonly userContextProvider?: UserContextProvider,
12+
) {}
13+
14+
async build(input: {
15+
base: BaseAgentTurnInput;
16+
turnId: string;
17+
}): Promise<AgentTurnContext> {
18+
const adminforth = this.getAdminforth();
19+
20+
return {
21+
adminUser: input.base.adminUser,
22+
sessionId: input.base.sessionId,
23+
turnId: input.turnId,
24+
abortSignal: input.base.abortSignal,
25+
currentPage: input.base.currentPage,
26+
chatSurface: input.base.chatSurface,
27+
userTimeZone:
28+
input.base.userTimeZone
29+
?? await this.userContextProvider?.getUserTimeZone(input.base.adminUser)
30+
?? "UTC",
31+
adminPublicOrigin:
32+
input.base.adminPublicOrigin
33+
?? adminforth.config.baseUrlSlashed,
34+
};
35+
}
36+
}

agent/turn/turnTypes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export type BaseAgentTurnInput = {
1010
prompt: string;
1111
sessionId: string;
1212
modeName?: string | null;
13-
userTimeZone: string;
13+
userTimeZone?: string;
1414
currentPage?: CurrentPageContext;
1515
chatSurface?: string;
1616
adminPublicOrigin?: string;

agentTurnService.ts

Lines changed: 17 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,21 @@
11
import { logger } from "adminforth";
22
import { randomUUID } from "crypto";
3-
import type { IAdminForth } from "adminforth";
4-
import type { BaseCheckpointSaver } from "@langchain/langgraph";
53
import { AgentModelFactory } from "./agent/models/AgentModelFactory.js";
64
import { AgentModeResolver } from "./agent/models/AgentModeResolver.js";
75
import { createSequenceDebugCollector } from "./agent/middleware/sequenceDebug.js";
8-
import { AgentRuntimeFactory } from "./agent/runtime/AgentRuntimeFactory.js";
9-
import { AgentToolProvider } from "./agent/tools/AgentToolProvider.js";
6+
import { AgentRuntime } from "./agent/runtime/AgentRuntime.js";
7+
import { TurnContextBuilder } from "./agent/turn/TurnContextBuilder.js";
108
import { TurnLifecycleService } from "./agent/turn/TurnLifecycleService.js";
11-
import { TurnPersistenceService } from "./agent/turn/TurnPersistenceService.js";
129
import { TurnPromptBuilder } from "./agent/turn/TurnPromptBuilder.js";
1310
import { TurnStreamConsumer } from "./agent/turn/TurnStreamConsumer.js";
1411
import type {
1512
BaseAgentTurnInput,
16-
HandleSpeechTurnInput,
1713
HandleTurnInput,
1814
PreparedAgentTurn,
1915
RunAndPersistAgentResponseInput,
2016
RunAndPersistAgentResponseResult,
2117
} from "./agent/turn/turnTypes.js";
2218
import { getErrorMessage, isAbortError } from "./errors.js";
23-
import type { AgentSessionStore } from "./sessionStore.js";
24-
import { SpeechTurnService } from "./agent/speech/SpeechTurnService.js";
25-
import type { PluginOptions } from "./types.js";
2619

2720
export type {
2821
BaseAgentTurnInput,
@@ -32,73 +25,32 @@ export type {
3225
RunAndPersistAgentResponseResult,
3326
} from "./agent/turn/turnTypes.js";
3427

35-
type AgentTurnServiceOptions = {
36-
getAdminforth: () => IAdminForth;
37-
getPluginInstanceId: () => string;
38-
options: PluginOptions;
39-
sessionStore: AgentSessionStore;
40-
getCheckpointer: () => BaseCheckpointSaver;
41-
getInternalAgentResourceIds: () => string[];
42-
getAgentSystemPrompt: () => Promise<string>;
43-
};
44-
4528
export class AgentTurnService {
46-
private readonly modeResolver: AgentModeResolver;
47-
private readonly modelFactory: AgentModelFactory;
48-
private readonly runtimeFactory: AgentRuntimeFactory;
49-
private readonly lifecycle: TurnLifecycleService;
50-
private readonly promptBuilder: TurnPromptBuilder;
51-
private readonly streamConsumer = new TurnStreamConsumer();
52-
private readonly speechTurnService: SpeechTurnService;
53-
54-
constructor(private readonly serviceOptions: AgentTurnServiceOptions) {
55-
const toolProvider = new AgentToolProvider(
56-
serviceOptions.getAdminforth,
57-
serviceOptions.getInternalAgentResourceIds,
58-
);
59-
const persistence = new TurnPersistenceService(
60-
serviceOptions.getAdminforth,
61-
serviceOptions.options,
62-
);
63-
64-
this.modeResolver = new AgentModeResolver(serviceOptions.options);
65-
this.modelFactory = new AgentModelFactory(serviceOptions.options.maxTokens ?? 1000);
66-
this.runtimeFactory = new AgentRuntimeFactory(
67-
serviceOptions.getAdminforth,
68-
serviceOptions.getCheckpointer,
69-
toolProvider,
70-
() => `adminforth-agent-${serviceOptions.getPluginInstanceId()}`,
71-
);
72-
this.lifecycle = new TurnLifecycleService(serviceOptions.sessionStore, persistence);
73-
this.promptBuilder = new TurnPromptBuilder({
74-
getAdminforth: serviceOptions.getAdminforth,
75-
getAgentSystemPrompt: serviceOptions.getAgentSystemPrompt,
76-
});
77-
this.speechTurnService = new SpeechTurnService(
78-
this.runAndPersistAgentResponse.bind(this),
79-
);
80-
}
29+
constructor(
30+
private readonly lifecycle: TurnLifecycleService,
31+
private readonly contextBuilder: TurnContextBuilder,
32+
private readonly modeResolver: AgentModeResolver,
33+
private readonly modelFactory: AgentModelFactory,
34+
private readonly promptBuilder: TurnPromptBuilder,
35+
private readonly runtime: AgentRuntime,
36+
private readonly streamConsumer: TurnStreamConsumer,
37+
) {}
8138

8239
private async prepareTurn(input: BaseAgentTurnInput): Promise<PreparedAgentTurn> {
8340
const sequenceDebugCollector = createSequenceDebugCollector();
8441
const { turnId, previousUserMessages } = await this.lifecycle.start(input);
42+
const context = await this.contextBuilder.build({
43+
base: input,
44+
turnId,
45+
});
8546

8647
return {
8748
prompt: input.prompt,
8849
sessionId: input.sessionId,
8950
turnId,
9051
previousUserMessages,
9152
modeName: input.modeName,
92-
context: {
93-
adminUser: input.adminUser,
94-
userTimeZone: input.userTimeZone,
95-
sessionId: input.sessionId,
96-
turnId,
97-
abortSignal: input.abortSignal,
98-
currentPage: input.currentPage,
99-
chatSurface: input.chatSurface,
100-
adminPublicOrigin: input.adminPublicOrigin,
101-
},
53+
context,
10254
observability: {
10355
emit: undefined,
10456
sequenceDebugSink: sequenceDebugCollector,
@@ -119,8 +71,7 @@ export class AgentTurnService {
11971
abortSignal: input.context.abortSignal,
12072
}),
12173
]);
122-
const runtime = this.runtimeFactory.create();
123-
const stream = await runtime.stream({
74+
const stream = await this.runtime.stream({
12475
models,
12576
messages,
12677
context: input.context,
@@ -214,8 +165,4 @@ export class AgentTurnService {
214165

215166
return agentResponse;
216167
}
217-
218-
async handleSpeechTurn(input: HandleSpeechTurnInput) {
219-
return this.speechTurnService.handle(input);
220-
}
221168
}

index.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ import type { AgentEndpointsContext } from "./endpoints/context.js";
1818
import { AgentSessionStore } from "./sessionStore.js";
1919
import { ChatSurfaceService } from "./chatSurfaceService.js";
2020
import { AgentTurnService } from "./agentTurnService.js";
21+
import { AgentModelFactory } from "./agent/models/AgentModelFactory.js";
22+
import { AgentModeResolver } from "./agent/models/AgentModeResolver.js";
23+
import { AgentRuntime } from "./agent/runtime/AgentRuntime.js";
24+
import { SpeechTurnService } from "./agent/speech/SpeechTurnService.js";
25+
import { AgentToolProvider } from "./agent/tools/AgentToolProvider.js";
26+
import { TurnContextBuilder } from "./agent/turn/TurnContextBuilder.js";
27+
import { TurnLifecycleService } from "./agent/turn/TurnLifecycleService.js";
28+
import { TurnPersistenceService } from "./agent/turn/TurnPersistenceService.js";
29+
import { TurnPromptBuilder } from "./agent/turn/TurnPromptBuilder.js";
30+
import { TurnStreamConsumer } from "./agent/turn/TurnStreamConsumer.js";
2131

2232
export type { AgentEvent, AgentEventEmitter } from "./agentEvents.js";
2333

@@ -27,6 +37,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
2737
private checkpointer: BaseCheckpointSaver | null = null;
2838
private sessionStore: AgentSessionStore;
2939
private agentTurnService: AgentTurnService;
40+
private speechTurnService: SpeechTurnService;
3041
private chatSurfaceService: ChatSurfaceService;
3142
private parseBody<T>(
3243
schema: z.ZodType<T>,
@@ -62,15 +73,32 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
6273
super(options, import.meta.url);
6374
this.options = options;
6475
this.sessionStore = new AgentSessionStore(() => this.adminforth, this.options);
65-
this.agentTurnService = new AgentTurnService({
76+
const toolProvider = new AgentToolProvider(
77+
() => this.adminforth,
78+
this.getInternalAgentResourceIds.bind(this),
79+
);
80+
const runtime = new AgentRuntime({
81+
name: `adminforth-agent-${this.pluginInstanceId}`,
6682
getAdminforth: () => this.adminforth,
67-
getPluginInstanceId: () => this.pluginInstanceId,
68-
options: this.options,
69-
sessionStore: this.sessionStore,
7083
getCheckpointer: this.getCheckpointer.bind(this),
71-
getInternalAgentResourceIds: this.getInternalAgentResourceIds.bind(this),
72-
getAgentSystemPrompt: () => this.agentSystemPromptPromise,
84+
toolProvider,
7385
});
86+
const persistence = new TurnPersistenceService(() => this.adminforth, this.options);
87+
this.agentTurnService = new AgentTurnService(
88+
new TurnLifecycleService(this.sessionStore, persistence),
89+
new TurnContextBuilder(() => this.adminforth),
90+
new AgentModeResolver(this.options),
91+
new AgentModelFactory(this.options.maxTokens ?? 1000),
92+
new TurnPromptBuilder({
93+
getAdminforth: () => this.adminforth,
94+
getAgentSystemPrompt: () => this.agentSystemPromptPromise,
95+
}),
96+
runtime,
97+
new TurnStreamConsumer(),
98+
);
99+
this.speechTurnService = new SpeechTurnService(
100+
this.agentTurnService.runAndPersistAgentResponse.bind(this.agentTurnService),
101+
);
74102
this.chatSurfaceService = new ChatSurfaceService(
75103
() => this.adminforth,
76104
this.options,
@@ -146,7 +174,7 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
146174
options: this.options,
147175
parseBody: this.parseBody.bind(this),
148176
handleTurn: this.agentTurnService.handleTurn.bind(this.agentTurnService),
149-
handleSpeechTurn: this.agentTurnService.handleSpeechTurn.bind(this.agentTurnService),
177+
handleSpeechTurn: this.speechTurnService.handle.bind(this.speechTurnService),
150178
runAndPersistAgentResponse: this.agentTurnService.runAndPersistAgentResponse.bind(this.agentTurnService),
151179
getSessionTurns: this.sessionStore.getSessionTurns.bind(this.sessionStore),
152180
createNewTurn: this.sessionStore.createNewTurn.bind(this.sessionStore),

0 commit comments

Comments
 (0)