mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-03-29 21:33:54 +00:00
feat: new integration
This commit is contained in:
parent
0f62ba8dd5
commit
8e05565e84
@ -25,6 +25,7 @@ class ToolType(str, Enum):
|
||||
CODER_AGENT = "coder_agent"
|
||||
DATABASE_SCRIPT = "database_script"
|
||||
API_FUNCTION = "api_function"
|
||||
PROMPT = "prompt"
|
||||
|
||||
|
||||
class FunctionRequestType(str, Enum):
|
||||
|
||||
@ -6,9 +6,10 @@ SDK Structure:
|
||||
- agent_functions: API Functions (with request_type)
|
||||
- connections: Provider connections (openai, anthropic, etc.)
|
||||
- agents: TF workspace agents
|
||||
- prompts: Prompt templates (with available_to_agents mapping)
|
||||
"""
|
||||
|
||||
from typing import Any, Optional
|
||||
from typing import Any, Optional, List
|
||||
|
||||
from pydantic import BaseModel
|
||||
from toothfairyai.types import AgentFunction
|
||||
@ -45,11 +46,23 @@ class SyncedTool(BaseModel):
|
||||
llm_provider: Optional[str] = None
|
||||
|
||||
|
||||
class SyncedPrompt(BaseModel):
|
||||
"""A prompt template synced from ToothFairyAI workspace."""
|
||||
|
||||
id: str
|
||||
label: str
|
||||
interpolation_string: str
|
||||
prompt_type: Optional[str] = None
|
||||
available_to_agents: Optional[List[str]] = None
|
||||
description: Optional[str] = None
|
||||
|
||||
|
||||
class ToolSyncResult(BaseModel):
|
||||
"""Result of tool sync operation."""
|
||||
|
||||
success: bool
|
||||
tools: list[SyncedTool] = []
|
||||
prompts: list[SyncedPrompt] = []
|
||||
by_type: dict[str, int] = {}
|
||||
error: Optional[str] = None
|
||||
|
||||
@ -149,6 +162,26 @@ def parse_agent(agent) -> SyncedTool:
|
||||
)
|
||||
|
||||
|
||||
def parse_prompt(prompt) -> SyncedPrompt:
|
||||
"""
|
||||
Parse Prompt from SDK into SyncedPrompt.
|
||||
|
||||
Args:
|
||||
prompt: Prompt from TF SDK
|
||||
|
||||
Returns:
|
||||
SyncedPrompt instance
|
||||
"""
|
||||
return SyncedPrompt(
|
||||
id=prompt.id,
|
||||
label=prompt.label,
|
||||
interpolation_string=prompt.interpolation_string,
|
||||
prompt_type=getattr(prompt, 'prompt_type', None),
|
||||
available_to_agents=getattr(prompt, 'available_to_agents', None),
|
||||
description=getattr(prompt, 'description', None),
|
||||
)
|
||||
|
||||
|
||||
def sync_tools(config: TFConfig) -> ToolSyncResult:
|
||||
"""
|
||||
Sync all tools from ToothFairyAI workspace using SDK.
|
||||
@ -157,12 +190,13 @@ def sync_tools(config: TFConfig) -> ToolSyncResult:
|
||||
- Agent Functions (API Functions with request_type)
|
||||
- Agent Skills (functions with is_agent_skill=True)
|
||||
- Coder Agents (agents with mode='coder')
|
||||
- Prompts (prompt templates with available_to_agents mapping)
|
||||
|
||||
Args:
|
||||
config: TFConfig instance
|
||||
|
||||
Returns:
|
||||
ToolSyncResult with synced tools
|
||||
ToolSyncResult with synced tools and prompts
|
||||
"""
|
||||
try:
|
||||
client = config.get_client()
|
||||
@ -180,14 +214,26 @@ def sync_tools(config: TFConfig) -> ToolSyncResult:
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
# Sync prompts
|
||||
prompts = []
|
||||
try:
|
||||
prompts_result = client.prompts.list()
|
||||
prompts = [parse_prompt(p) for p in prompts_result.items]
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
by_type = {}
|
||||
for tool in tools:
|
||||
type_name = tool.tool_type.value
|
||||
by_type[type_name] = by_type.get(type_name, 0) + 1
|
||||
|
||||
if prompts:
|
||||
by_type['prompt'] = len(prompts)
|
||||
|
||||
return ToolSyncResult(
|
||||
success=True,
|
||||
tools=tools,
|
||||
prompts=prompts,
|
||||
by_type=by_type,
|
||||
)
|
||||
|
||||
|
||||
@ -306,6 +306,37 @@ export namespace Agent {
|
||||
}
|
||||
}
|
||||
|
||||
export interface TFPrompt {
|
||||
id: string
|
||||
label: string
|
||||
interpolation_string: string
|
||||
available_to_agents?: string[]
|
||||
}
|
||||
|
||||
async function loadTFPrompts(): Promise<TFPrompt[]> {
|
||||
const toolsPath = path.join(os.homedir(), ".tfcode", "tools.json")
|
||||
try {
|
||||
const content = await Bun.file(toolsPath).text()
|
||||
const data = JSON.parse(content)
|
||||
if (!data.success || !data.prompts) return []
|
||||
return data.prompts as TFPrompt[]
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export async function getPromptForAgent(agentId: string): Promise<TFPrompt | null> {
|
||||
const prompts = await loadTFPrompts()
|
||||
return prompts.find((p) => p.available_to_agents?.includes(agentId)) ?? null
|
||||
}
|
||||
|
||||
export async function getPromptForAgentName(agentName: string): Promise<TFPrompt | null> {
|
||||
const agents = await loadTFCoderAgents()
|
||||
const agent = agents.find((a) => a.name === agentName)
|
||||
if (!agent?.options?.tf_agent_id) return null
|
||||
return getPromptForAgent(agent.options.tf_agent_id)
|
||||
}
|
||||
|
||||
export async function list() {
|
||||
const cfg = await Config.get()
|
||||
const localAgents = await state()
|
||||
|
||||
@ -14,7 +14,7 @@ const printError = (msg: string) => UI.error(msg)
|
||||
const success = (msg: string) => UI.println(UI.Style.TEXT_SUCCESS_BOLD + msg + UI.Style.TEXT_NORMAL)
|
||||
const info = (msg: string) => UI.println(UI.Style.TEXT_NORMAL + msg)
|
||||
|
||||
type ToolType = "mcp_server" | "agent_skill" | "database_script" | "api_function"
|
||||
type ToolType = "mcp_server" | "agent_skill" | "database_script" | "api_function" | "coder_agent" | "prompt"
|
||||
|
||||
interface SyncedTool {
|
||||
id: string
|
||||
@ -28,11 +28,27 @@ interface SyncedTool {
|
||||
url?: string
|
||||
tools: string[]
|
||||
auth_via: string
|
||||
interpolation_string?: string
|
||||
goals?: string
|
||||
temperature?: number
|
||||
max_tokens?: number
|
||||
llm_base_model?: string
|
||||
llm_provider?: string
|
||||
}
|
||||
|
||||
interface SyncedPrompt {
|
||||
id: string
|
||||
label: string
|
||||
interpolation_string: string
|
||||
prompt_type?: string
|
||||
available_to_agents?: string[]
|
||||
description?: string
|
||||
}
|
||||
|
||||
interface ToolSyncResult {
|
||||
success: boolean
|
||||
tools: SyncedTool[]
|
||||
prompts: SyncedPrompt[]
|
||||
by_type: Record<string, number>
|
||||
error?: string
|
||||
}
|
||||
@ -101,12 +117,30 @@ try:
|
||||
"request_type": tool.request_type.value if tool.request_type else None,
|
||||
"url": tool.url,
|
||||
"tools": tool.tools,
|
||||
"auth_via": tool.auth_via
|
||||
"auth_via": tool.auth_via,
|
||||
"interpolation_string": tool.interpolation_string,
|
||||
"goals": tool.goals,
|
||||
"temperature": tool.temperature,
|
||||
"max_tokens": tool.max_tokens,
|
||||
"llm_base_model": tool.llm_base_model,
|
||||
"llm_provider": tool.llm_provider
|
||||
})
|
||||
|
||||
prompts_data = []
|
||||
for prompt in result.prompts:
|
||||
prompts_data.append({
|
||||
"id": prompt.id,
|
||||
"label": prompt.label,
|
||||
"interpolation_string": prompt.interpolation_string,
|
||||
"prompt_type": prompt.prompt_type,
|
||||
"available_to_agents": prompt.available_to_agents,
|
||||
"description": prompt.description
|
||||
})
|
||||
|
||||
print(json.dumps({
|
||||
"success": result.success,
|
||||
"tools": tools_data,
|
||||
"prompts": prompts_data,
|
||||
"by_type": result.by_type,
|
||||
"error": result.error
|
||||
}))
|
||||
@ -138,7 +172,13 @@ try:
|
||||
"request_type": tool.request_type.value if tool.request_type else None,
|
||||
"url": tool.url,
|
||||
"tools": tool.tools,
|
||||
"auth_via": tool.auth_via
|
||||
"auth_via": tool.auth_via,
|
||||
"interpolation_string": tool.interpolation_string,
|
||||
"goals": tool.goals,
|
||||
"temperature": tool.temperature,
|
||||
"max_tokens": tool.max_tokens,
|
||||
"llm_base_model": tool.llm_base_model,
|
||||
"llm_provider": tool.llm_provider
|
||||
})
|
||||
|
||||
print(json.dumps({
|
||||
@ -353,6 +393,8 @@ const ToolsListCommand = cmd({
|
||||
agent_skill: "Skill",
|
||||
database_script: "DB",
|
||||
api_function: "API",
|
||||
coder_agent: "Coder Agent",
|
||||
prompt: "Prompt",
|
||||
}[tool.tool_type]
|
||||
|
||||
info(` ${tool.name}`)
|
||||
|
||||
@ -13,6 +13,8 @@ import { useTerminalDimensions } from "@opentui/solid"
|
||||
import { Locale } from "@/util/locale"
|
||||
import type { PromptInfo } from "./history"
|
||||
import { useFrecency } from "./frecency"
|
||||
import { Agent } from "@/agent/agent"
|
||||
import { useLocal } from "@tui/context/local"
|
||||
|
||||
function removeLineRange(input: string) {
|
||||
const hashIndex = input.lastIndexOf("#")
|
||||
@ -77,6 +79,7 @@ export function Autocomplete(props: {
|
||||
}) {
|
||||
const sdk = useSDK()
|
||||
const sync = useSync()
|
||||
const local = useLocal()
|
||||
const command = useCommandDialog()
|
||||
const { theme } = useTheme()
|
||||
const dimensions = useTerminalDimensions()
|
||||
@ -353,6 +356,42 @@ export function Autocomplete(props: {
|
||||
)
|
||||
})
|
||||
|
||||
const tfPrompts = createMemo(() => {
|
||||
if (!store.visible || store.visible === "/") return []
|
||||
|
||||
const currentAgent = local.agent.current()
|
||||
const agentId = currentAgent.options?.tf_agent_id as string | undefined
|
||||
if (!agentId) return []
|
||||
|
||||
const options: AutocompleteOption[] = []
|
||||
const width = props.anchor().width - 4
|
||||
|
||||
const prompts = sync.data.prompts || []
|
||||
|
||||
for (const prompt of prompts) {
|
||||
const isAvailable =
|
||||
!prompt.available_to_agents ||
|
||||
prompt.available_to_agents.length === 0 ||
|
||||
prompt.available_to_agents.includes(agentId)
|
||||
|
||||
if (isAvailable) {
|
||||
options.push({
|
||||
display: Locale.truncateMiddle("@" + prompt.label, width),
|
||||
value: prompt.label,
|
||||
description: "Prompt template",
|
||||
onSelect: () => {
|
||||
const cursor = props.input().logicalCursor
|
||||
props.input().deleteRange(0, 0, cursor.row, cursor.col)
|
||||
props.input().insertText(prompt.interpolation_string)
|
||||
props.input().cursorOffset = Bun.stringWidth(prompt.interpolation_string)
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
})
|
||||
|
||||
const commands = createMemo((): AutocompleteOption[] => {
|
||||
const results: AutocompleteOption[] = [...command.slashes()]
|
||||
|
||||
@ -386,9 +425,12 @@ export function Autocomplete(props: {
|
||||
const filesValue = files()
|
||||
const agentsValue = agents()
|
||||
const commandsValue = commands()
|
||||
const promptsValue = tfPrompts()
|
||||
|
||||
const mixed: AutocompleteOption[] =
|
||||
store.visible === "@" ? [...agentsValue, ...(filesValue || []), ...mcpResources()] : [...commandsValue]
|
||||
store.visible === "@"
|
||||
? [...agentsValue, ...promptsValue, ...(filesValue || []), ...mcpResources()]
|
||||
: [...commandsValue]
|
||||
|
||||
const searchValue = search()
|
||||
|
||||
|
||||
@ -29,6 +29,8 @@ import { batch, onMount } from "solid-js"
|
||||
import { Log } from "@/util/log"
|
||||
import type { Path } from "@opencode-ai/sdk"
|
||||
import type { Workspace } from "@opencode-ai/sdk/v2"
|
||||
import path from "path"
|
||||
import os from "os"
|
||||
|
||||
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||
name: "Sync",
|
||||
@ -75,6 +77,12 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||
vcs: VcsInfo | undefined
|
||||
path: Path
|
||||
workspaceList: Workspace[]
|
||||
prompts: Array<{
|
||||
id: string
|
||||
label: string
|
||||
interpolation_string: string
|
||||
available_to_agents?: string[]
|
||||
}>
|
||||
}>({
|
||||
provider_next: {
|
||||
all: [],
|
||||
@ -103,6 +111,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||
vcs: undefined,
|
||||
path: { state: "", config: "", worktree: "", directory: "" },
|
||||
workspaceList: [],
|
||||
prompts: [],
|
||||
})
|
||||
|
||||
const sdk = useSDK()
|
||||
@ -113,6 +122,19 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||
setStore("workspaceList", reconcile(result.data))
|
||||
}
|
||||
|
||||
async function loadTFPrompts() {
|
||||
const toolsPath = path.join(os.homedir(), ".tfcode", "tools.json")
|
||||
try {
|
||||
const content = await Bun.file(toolsPath).text()
|
||||
const data = JSON.parse(content)
|
||||
if (data.success && data.prompts) {
|
||||
setStore("prompts", reconcile(data.prompts))
|
||||
}
|
||||
} catch {
|
||||
// File doesn't exist or is invalid, that's OK
|
||||
}
|
||||
}
|
||||
|
||||
sdk.event.listen((e) => {
|
||||
const event = e.details
|
||||
switch (event.type) {
|
||||
@ -423,6 +445,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||
sdk.client.vcs.get().then((x) => setStore("vcs", reconcile(x.data))),
|
||||
sdk.client.path.get().then((x) => setStore("path", reconcile(x.data!))),
|
||||
syncWorkspaces(),
|
||||
loadTFPrompts(),
|
||||
]).then(() => {
|
||||
setStore("status", "complete")
|
||||
})
|
||||
|
||||
@ -70,7 +70,7 @@ export namespace LLM {
|
||||
const system: string[] = []
|
||||
|
||||
// Build highlighted agent instructions for ToothFairyAI agents
|
||||
const tfHighlightedInstructions = buildTFAgentInstructions(input.agent)
|
||||
const tfHighlightedInstructions = await buildTFAgentInstructions(input.agent)
|
||||
|
||||
system.push(
|
||||
[
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user