-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Expand file tree
/
Copy pathknowledge-base.ts
More file actions
130 lines (115 loc) · 4.88 KB
/
knowledge-base.ts
File metadata and controls
130 lines (115 loc) · 4.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import { createLogger } from '@sim/logger'
import { Database, Loader2, MinusCircle, PlusCircle, XCircle } from 'lucide-react'
import {
BaseClientTool,
type BaseClientToolMetadata,
ClientToolCallState,
} from '@/lib/copilot/tools/client/base-tool'
import {
ExecuteResponseSuccessSchema,
type KnowledgeBaseArgs,
} from '@/lib/copilot/tools/shared/schemas'
import { useCopilotStore } from '@/stores/panel/copilot/store'
/**
* Client tool for knowledge base operations
*/
export class KnowledgeBaseClientTool extends BaseClientTool {
static readonly id = 'knowledge_base'
constructor(toolCallId: string) {
super(toolCallId, KnowledgeBaseClientTool.id, KnowledgeBaseClientTool.metadata)
}
/**
* Only show interrupt for create operation
*/
getInterruptDisplays(): BaseClientToolMetadata['interrupt'] | undefined {
const toolCallsById = useCopilotStore.getState().toolCallsById
const toolCall = toolCallsById[this.toolCallId]
const params = toolCall?.params as KnowledgeBaseArgs | undefined
// Only require confirmation for create operation
if (params?.operation === 'create') {
const name = params?.args?.name || 'new knowledge base'
return {
accept: { text: `Create "${name}"`, icon: PlusCircle },
reject: { text: 'Skip', icon: XCircle },
}
}
// No interrupt for list, get, query - auto-execute
return undefined
}
static readonly metadata: BaseClientToolMetadata = {
displayNames: {
[ClientToolCallState.generating]: { text: 'Accessing knowledge base', icon: Loader2 },
[ClientToolCallState.pending]: { text: 'Accessing knowledge base', icon: Loader2 },
[ClientToolCallState.executing]: { text: 'Accessing knowledge base', icon: Loader2 },
[ClientToolCallState.success]: { text: 'Accessed knowledge base', icon: Database },
[ClientToolCallState.error]: { text: 'Failed to access knowledge base', icon: XCircle },
[ClientToolCallState.aborted]: { text: 'Aborted knowledge base access', icon: MinusCircle },
[ClientToolCallState.rejected]: { text: 'Skipped knowledge base access', icon: MinusCircle },
},
getDynamicText: (params: Record<string, any>, state: ClientToolCallState) => {
const operation = params?.operation as string | undefined
const name = params?.args?.name as string | undefined
const opVerbs: Record<string, { active: string; past: string; pending?: string }> = {
create: {
active: 'Creating knowledge base',
past: 'Created knowledge base',
pending: name ? `Create knowledge base "${name}"?` : 'Create knowledge base?',
},
list: { active: 'Listing knowledge bases', past: 'Listed knowledge bases' },
get: { active: 'Getting knowledge base', past: 'Retrieved knowledge base' },
query: { active: 'Querying knowledge base', past: 'Queried knowledge base' },
}
const defaultVerb: { active: string; past: string; pending?: string } = {
active: 'Accessing knowledge base',
past: 'Accessed knowledge base',
}
const verb = operation ? opVerbs[operation] || defaultVerb : defaultVerb
if (state === ClientToolCallState.success) {
return verb.past
}
if (state === ClientToolCallState.pending && verb.pending) {
return verb.pending
}
if (
state === ClientToolCallState.generating ||
state === ClientToolCallState.pending ||
state === ClientToolCallState.executing
) {
return verb.active
}
return undefined
},
}
async handleReject(): Promise<void> {
await super.handleReject()
this.setState(ClientToolCallState.rejected)
}
async handleAccept(args?: KnowledgeBaseArgs): Promise<void> {
await this.execute(args)
}
async execute(args?: KnowledgeBaseArgs): Promise<void> {
const logger = createLogger('KnowledgeBaseClientTool')
try {
this.setState(ClientToolCallState.executing)
const payload: KnowledgeBaseArgs = { ...(args || { operation: 'list' }) }
const res = await fetch('/api/copilot/execute-copilot-server-tool', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ toolName: 'knowledge_base', payload }),
})
if (!res.ok) {
const txt = await res.text().catch(() => '')
throw new Error(txt || `Server error (${res.status})`)
}
const json = await res.json()
const parsed = ExecuteResponseSuccessSchema.parse(json)
this.setState(ClientToolCallState.success)
await this.markToolComplete(200, 'Knowledge base operation completed', parsed.result)
this.setState(ClientToolCallState.success)
} catch (e: any) {
logger.error('execute failed', { message: e?.message })
this.setState(ClientToolCallState.error)
await this.markToolComplete(500, e?.message || 'Failed to access knowledge base')
}
}
}