2025-10-21 20:05:39 +08:00
import type { AgentHistory , ExecutionResult , PageAgent } from '@/PageAgent'
2025-09-29 16:33:15 +08:00
import type { DomConfig } from '@/dom'
import type { SupportedLanguage } from '@/i18n'
2025-10-21 19:29:13 +08:00
import type { PageAgentTool } from '@/tools'
2025-09-29 16:33:15 +08:00
2025-10-20 22:44:02 +08:00
import {
DEFAULT_API_KEY ,
DEFAULT_BASE_URL ,
DEFAULT_MAX_TOKENS ,
DEFAULT_MODEL_NAME ,
DEFAULT_TEMPERATURE ,
LLM_MAX_RETRIES ,
} from './constants'
2025-10-10 17:46:40 +08:00
2025-10-17 18:43:41 +08:00
export interface LLMConfig {
baseURL? : string
apiKey? : string
2025-10-21 15:13:16 +08:00
model? : string
2025-10-17 18:43:41 +08:00
temperature? : number
maxTokens? : number
maxRetries? : number
}
2025-09-29 16:33:15 +08:00
export interface UIConfig {
// theme?: 'light' | 'dark'
language? : SupportedLanguage
2025-10-21 19:29:13 +08:00
/**
* Custom tools to extend PageAgent capabilities
* @experimental
* @note You can also override or remove internal tools by using the same name.
* @see [tools](../tools/index.ts)
*
* @example
2025-10-21 20:05:39 +08:00
* // override internal tool
2025-10-21 19:29:13 +08:00
* import { tool } from 'page-agent'
* const customTools = {
* ask_user: tool({
* description:
* 'Ask the user or parent model a question and wait for their answer. Use this if you need more information or clarification.',
* inputSchema: zod.object({
* question: zod.string(),
* }),
* execute: async function (this: PageAgent, input) {
* const answer = await do_some_thing(input.question)
* return `✅ Received user answer: ${answer}` + (await getSystemInfo())
* },
* })
* }
2025-10-21 20:05:39 +08:00
*
* @example
* // remove internal tool
* const customTools = {
* ask_user: null // never ask user questions
* }
2025-10-21 19:29:13 +08:00
*/
customTools? : Record < string , PageAgentTool | null >
2025-10-21 19:39:06 +08:00
2025-10-21 20:05:39 +08:00
// lifecycle hooks
2025-10-21 21:31:35 +08:00
// @todo: use event instead of hooks
2025-10-21 19:39:06 +08:00
onBeforeStep ?: ( this : PageAgent , stepCnt : number ) => Promise < void > | void
onAfterStep ?: ( this : PageAgent , stepCnt : number , history : AgentHistory []) => Promise < void > | void
2025-10-21 21:31:35 +08:00
onBeforeTask ?: ( this : PageAgent ) => Promise < void > | void
onAfterTask ?: ( this : PageAgent , result : ExecutionResult ) => Promise < void > | void
/**
* @note this hook can block the disposal process
* @note when dispose caused by page unload, `reason` will be 'PAGE_UNLOADING'. this method CANNOT block the unload process. async operations may be cut.
*/
onDispose ?: ( this : PageAgent , reason? : string ) => void
2025-10-21 20:05:39 +08:00
// page behavior hooks
2025-10-21 21:31:35 +08:00
/**
* TODO: @unimplemented
* hook when action causes a new page to be opened
* @note PageAgent will try to detect new pages and decide if it's caused by an action. But not very reliable.
*/
onNewPageOpen ?: ( this : PageAgent , url : string ) => Promise < void > | void
/**
* TODO: @unimplemented
* try to navigate to a new page instead of opening a new tab/window.
* @note will unload the current page when a action tries to open a new page. so that things keep in the same tab/window.
*/
experimentalPreventNewPage? : boolean
2025-09-29 16:33:15 +08:00
}
export type PageAgentConfig = LLMConfig & DomConfig & UIConfig
2025-10-10 17:46:40 +08:00
export function parseLLMConfig ( config : LLMConfig ) : Required < LLMConfig > {
return {
baseURL : config.baseURL ?? DEFAULT_BASE_URL ,
apiKey : config.apiKey ?? DEFAULT_API_KEY ,
2025-10-21 15:13:16 +08:00
model : config.model ?? DEFAULT_MODEL_NAME ,
2025-10-20 22:44:02 +08:00
temperature : config.temperature ?? DEFAULT_TEMPERATURE ,
maxTokens : config.maxTokens ?? DEFAULT_MAX_TOKENS ,
2025-10-10 17:46:40 +08:00
maxRetries : config.maxRetries ?? LLM_MAX_RETRIES ,
}
}