Lifei/sub recipe desktop temp (#3576)
This commit is contained in:
@@ -2,6 +2,7 @@ use goose::agents::subagent_execution_tool::lib::TaskStatus;
|
||||
use goose::agents::subagent_execution_tool::notification_events::{
|
||||
TaskExecutionNotificationEvent, TaskInfo,
|
||||
};
|
||||
use goose::utils::safe_truncate;
|
||||
use serde_json::Value;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
@@ -18,7 +19,7 @@ static INITIAL_SHOWN: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
fn format_result_data_for_display(result_data: &Value) -> String {
|
||||
match result_data {
|
||||
Value::String(s) => strip_ansi_codes(s),
|
||||
Value::String(s) => s.to_string(),
|
||||
Value::Object(obj) => {
|
||||
if let Some(partial_output) = obj.get("partial_output").and_then(|v| v.as_str()) {
|
||||
format!("Partial output: {}", partial_output)
|
||||
@@ -45,53 +46,7 @@ fn process_output_for_display(output: &str) -> String {
|
||||
};
|
||||
|
||||
let clean_output = recent_lines.join(" ... ");
|
||||
let stripped = strip_ansi_codes(&clean_output);
|
||||
truncate_with_ellipsis(&stripped, OUTPUT_PREVIEW_LENGTH)
|
||||
}
|
||||
|
||||
fn truncate_with_ellipsis(text: &str, max_len: usize) -> String {
|
||||
if text.len() > max_len {
|
||||
let mut end = max_len.saturating_sub(3);
|
||||
while end > 0 && !text.is_char_boundary(end) {
|
||||
end -= 1;
|
||||
}
|
||||
format!("{}...", &text[..end])
|
||||
} else {
|
||||
text.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_ansi_codes(text: &str) -> String {
|
||||
let mut result = String::new();
|
||||
let mut chars = text.chars();
|
||||
|
||||
while let Some(ch) = chars.next() {
|
||||
if ch == '\x1b' {
|
||||
if let Some(next_ch) = chars.next() {
|
||||
if next_ch == '[' {
|
||||
// This is an ANSI escape sequence, consume until alphabetic character
|
||||
loop {
|
||||
match chars.next() {
|
||||
Some(c) if c.is_ascii_alphabetic() => break,
|
||||
Some(_) => continue,
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Not an ANSI sequence, keep both characters
|
||||
result.push(ch);
|
||||
result.push(next_ch);
|
||||
}
|
||||
} else {
|
||||
// End of string after \x1b
|
||||
result.push(ch);
|
||||
}
|
||||
} else {
|
||||
result.push(ch);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
safe_truncate(&clean_output, OUTPUT_PREVIEW_LENGTH)
|
||||
}
|
||||
|
||||
pub fn format_task_execution_notification(
|
||||
@@ -233,7 +188,7 @@ fn format_task_display(task: &TaskInfo) -> String {
|
||||
|
||||
if matches!(task.status, TaskStatus::Failed) {
|
||||
if let Some(error) = &task.error {
|
||||
let error_preview = truncate_with_ellipsis(error, 80);
|
||||
let error_preview = safe_truncate(error, 80);
|
||||
task_display.push_str(&format!(
|
||||
" ⚠️ {}{}\n",
|
||||
error_preview.replace('\n', " "),
|
||||
|
||||
@@ -4,34 +4,6 @@ use goose::agents::subagent_execution_tool::notification_events::{
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn test_strip_ansi_codes() {
|
||||
assert_eq!(strip_ansi_codes("hello world"), "hello world");
|
||||
assert_eq!(strip_ansi_codes("\x1b[31mred text\x1b[0m"), "red text");
|
||||
assert_eq!(
|
||||
strip_ansi_codes("\x1b[1;32mbold green\x1b[0m"),
|
||||
"bold green"
|
||||
);
|
||||
assert_eq!(
|
||||
strip_ansi_codes("normal\x1b[33myellow\x1b[0mnormal"),
|
||||
"normalyellownormal"
|
||||
);
|
||||
assert_eq!(strip_ansi_codes("\x1bhello"), "\x1bhello");
|
||||
assert_eq!(strip_ansi_codes("hello\x1b"), "hello\x1b");
|
||||
assert_eq!(strip_ansi_codes(""), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_truncate_with_ellipsis() {
|
||||
assert_eq!(truncate_with_ellipsis("hello", 10), "hello");
|
||||
assert_eq!(truncate_with_ellipsis("hello", 5), "hello");
|
||||
assert_eq!(truncate_with_ellipsis("hello world", 8), "hello...");
|
||||
assert_eq!(truncate_with_ellipsis("hello", 3), "...");
|
||||
assert_eq!(truncate_with_ellipsis("hello", 2), "...");
|
||||
assert_eq!(truncate_with_ellipsis("hello", 1), "...");
|
||||
assert_eq!(truncate_with_ellipsis("", 5), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_output_for_display() {
|
||||
assert_eq!(process_output_for_display("hello world"), "hello world");
|
||||
@@ -49,20 +21,15 @@ fn test_process_output_for_display() {
|
||||
assert!(result.len() <= 100);
|
||||
assert!(result.ends_with("..."));
|
||||
|
||||
let ansi_output = "\x1b[31mred line 1\x1b[0m\n\x1b[32mgreen line 2\x1b[0m";
|
||||
let result = process_output_for_display(ansi_output);
|
||||
assert_eq!(result, "red line 1 ... green line 2");
|
||||
|
||||
assert_eq!(process_output_for_display(""), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_result_data_for_display() {
|
||||
let string_val = json!("hello world");
|
||||
assert_eq!(format_result_data_for_display(&string_val), "hello world");
|
||||
|
||||
let ansi_string = json!("\x1b[31mred text\x1b[0m");
|
||||
assert_eq!(format_result_data_for_display(&ansi_string), "red text");
|
||||
assert_eq!(
|
||||
format_result_data_for_display(&json!("red text")),
|
||||
"red text"
|
||||
);
|
||||
|
||||
assert_eq!(format_result_data_for_display(&json!(true)), "true");
|
||||
assert_eq!(format_result_data_for_display(&json!(false)), "false");
|
||||
|
||||
@@ -305,6 +305,7 @@ derive_utoipa!(ResourceContents as ResourceContentsSchema);
|
||||
super::routes::config_management::providers,
|
||||
super::routes::config_management::upsert_permissions,
|
||||
super::routes::agent::get_tools,
|
||||
super::routes::agent::add_sub_recipes,
|
||||
super::routes::reply::confirm_permission,
|
||||
super::routes::context::manage_context,
|
||||
super::routes::session::list_sessions,
|
||||
@@ -394,6 +395,8 @@ derive_utoipa!(ResourceContents as ResourceContentsSchema);
|
||||
goose::recipe::SubRecipe,
|
||||
goose::agents::types::RetryConfig,
|
||||
goose::agents::types::SuccessCheck,
|
||||
super::routes::agent::AddSubRecipesRequest,
|
||||
super::routes::agent::AddSubRecipesResponse,
|
||||
))
|
||||
)]
|
||||
pub struct ApiDoc;
|
||||
|
||||
@@ -6,7 +6,6 @@ use axum::{
|
||||
routing::{get, post},
|
||||
Json, Router,
|
||||
};
|
||||
use goose::config::Config;
|
||||
use goose::config::PermissionManager;
|
||||
use goose::model::ModelConfig;
|
||||
use goose::providers::create;
|
||||
@@ -15,6 +14,7 @@ use goose::{
|
||||
agents::{extension::ToolInfo, extension_manager::get_parameter_names},
|
||||
config::permission::PermissionLevel,
|
||||
};
|
||||
use goose::{config::Config, recipe::SubRecipe};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
@@ -35,6 +35,16 @@ struct ExtendPromptResponse {
|
||||
success: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, utoipa::ToSchema)]
|
||||
pub struct AddSubRecipesRequest {
|
||||
sub_recipes: Vec<SubRecipe>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, utoipa::ToSchema)]
|
||||
pub struct AddSubRecipesResponse {
|
||||
success: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ProviderFile {
|
||||
name: String,
|
||||
@@ -88,6 +98,30 @@ async fn get_versions() -> Json<VersionsResponse> {
|
||||
})
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/agent/add_sub_recipes",
|
||||
request_body = AddSubRecipesRequest,
|
||||
responses(
|
||||
(status = 200, description = "added sub recipes to agent successfully", body = AddSubRecipesResponse),
|
||||
(status = 401, description = "Unauthorized - invalid secret key"),
|
||||
),
|
||||
)]
|
||||
async fn add_sub_recipes(
|
||||
State(state): State<Arc<AppState>>,
|
||||
headers: HeaderMap,
|
||||
Json(payload): Json<AddSubRecipesRequest>,
|
||||
) -> Result<Json<AddSubRecipesResponse>, StatusCode> {
|
||||
verify_secret_key(&headers, &state)?;
|
||||
|
||||
let agent = state
|
||||
.get_agent()
|
||||
.await
|
||||
.map_err(|_| StatusCode::PRECONDITION_FAILED)?;
|
||||
agent.add_sub_recipes(payload.sub_recipes.clone()).await;
|
||||
Ok(Json(AddSubRecipesResponse { success: true }))
|
||||
}
|
||||
|
||||
async fn extend_prompt(
|
||||
State(state): State<Arc<AppState>>,
|
||||
headers: HeaderMap,
|
||||
@@ -318,5 +352,6 @@ pub fn routes(state: Arc<AppState>) -> Router {
|
||||
post(update_router_tool_selector),
|
||||
)
|
||||
.route("/agent/session_config", post(update_session_config))
|
||||
.route("/agent/add_sub_recipes", post(add_sub_recipes))
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
@@ -193,15 +193,7 @@ async fn collect_results(
|
||||
expected_count: usize,
|
||||
) -> Vec<TaskResult> {
|
||||
let mut results = Vec::new();
|
||||
while let Some(mut result) = result_rx.recv().await {
|
||||
// Truncate data to 650 chars if needed
|
||||
if let Some(data) = result.data.as_mut() {
|
||||
if let Some(data_str) = data.as_str() {
|
||||
if data_str.len() > 650 {
|
||||
*data = serde_json::Value::String(format!("{}...", &data_str[..650]));
|
||||
}
|
||||
}
|
||||
}
|
||||
while let Some(result) = result_rx.recv().await {
|
||||
task_execution_tracker
|
||||
.complete_task(&result.task_id, result.clone())
|
||||
.await;
|
||||
|
||||
@@ -8,6 +8,7 @@ use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::agents::subagent_execution_tool::task_execution_tracker::TaskExecutionTracker;
|
||||
use crate::agents::subagent_execution_tool::task_types::{Task, TaskResult, TaskStatus};
|
||||
use crate::agents::subagent_execution_tool::utils::strip_ansi_codes;
|
||||
use crate::agents::subagent_handler::run_complete_subagent_task;
|
||||
use crate::agents::subagent_task_config::TaskConfig;
|
||||
|
||||
@@ -70,7 +71,7 @@ async fn get_task_result(
|
||||
if success {
|
||||
process_output(stdout_output)
|
||||
} else {
|
||||
Err(format!("Command failed:\n{}", stderr_output))
|
||||
Err(format!("Command failed:\n{}", &stderr_output))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,6 +225,7 @@ fn spawn_output_reader(
|
||||
let mut buffer = String::new();
|
||||
let mut lines = BufReader::new(reader).lines();
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
let line = strip_ansi_codes(&line);
|
||||
buffer.push_str(&line);
|
||||
buffer.push('\n');
|
||||
|
||||
|
||||
@@ -23,5 +23,38 @@ pub fn count_by_status(tasks: &HashMap<String, TaskInfo>) -> (usize, usize, usiz
|
||||
(total, pending, running, completed, failed)
|
||||
}
|
||||
|
||||
pub fn strip_ansi_codes(text: &str) -> String {
|
||||
let mut result = String::new();
|
||||
let mut chars = text.chars();
|
||||
|
||||
while let Some(ch) = chars.next() {
|
||||
if ch == '\x1b' {
|
||||
if let Some(next_ch) = chars.next() {
|
||||
if next_ch == '[' {
|
||||
// This is an ANSI escape sequence, consume until alphabetic character
|
||||
loop {
|
||||
match chars.next() {
|
||||
Some(c) if c.is_ascii_alphabetic() => break,
|
||||
Some(_) => continue,
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Not an ANSI sequence, keep both characters
|
||||
result.push(ch);
|
||||
result.push(next_ch);
|
||||
}
|
||||
} else {
|
||||
// End of string after \x1b
|
||||
result.push(ch);
|
||||
}
|
||||
} else {
|
||||
result.push(ch);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::agents::subagent_execution_tool::task_types::{Task, TaskInfo, TaskStatus};
|
||||
use crate::agents::subagent_execution_tool::utils::{count_by_status, get_task_name};
|
||||
use crate::agents::subagent_execution_tool::utils::{
|
||||
count_by_status, get_task_name, strip_ansi_codes,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -152,3 +154,24 @@ mod count_by_status {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod strip_ansi_codes {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_strip_ansi_codes() {
|
||||
assert_eq!(strip_ansi_codes("hello world"), "hello world");
|
||||
assert_eq!(strip_ansi_codes("\x1b[31mred text\x1b[0m"), "red text");
|
||||
assert_eq!(
|
||||
strip_ansi_codes("\x1b[1;32mbold green\x1b[0m"),
|
||||
"bold green"
|
||||
);
|
||||
assert_eq!(
|
||||
strip_ansi_codes("normal\x1b[33myellow\x1b[0mnormal"),
|
||||
"normalyellownormal"
|
||||
);
|
||||
assert_eq!(strip_ansi_codes("\x1bhello"), "\x1bhello");
|
||||
assert_eq!(strip_ansi_codes("hello\x1b"), "hello\x1b");
|
||||
assert_eq!(strip_ansi_codes(""), "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,39 @@
|
||||
"version": "1.1.0"
|
||||
},
|
||||
"paths": {
|
||||
"/agent/add_sub_recipes": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"super::routes::agent"
|
||||
],
|
||||
"operationId": "add_sub_recipes",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/AddSubRecipesRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "added sub recipes to agent successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/AddSubRecipesResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized - invalid secret key"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/agent/tools": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -1033,6 +1066,31 @@
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"AddSubRecipesRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"sub_recipes"
|
||||
],
|
||||
"properties": {
|
||||
"sub_recipes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/SubRecipe"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"AddSubRecipesResponse": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"success"
|
||||
],
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Annotations": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
|
||||
import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch';
|
||||
import type { GetToolsData, GetToolsResponse, ReadAllConfigData, ReadAllConfigResponse, BackupConfigData, BackupConfigResponse, GetExtensionsData, GetExtensionsResponse, AddExtensionData, AddExtensionResponse, RemoveExtensionData, RemoveExtensionResponse, InitConfigData, InitConfigResponse, UpsertPermissionsData, UpsertPermissionsResponse, ProvidersData, ProvidersResponse2, ReadConfigData, RecoverConfigData, RecoverConfigResponse, RemoveConfigData, RemoveConfigResponse, UpsertConfigData, UpsertConfigResponse, ValidateConfigData, ValidateConfigResponse, ConfirmPermissionData, ManageContextData, ManageContextResponse, CreateRecipeData, CreateRecipeResponse2, DecodeRecipeData, DecodeRecipeResponse2, EncodeRecipeData, EncodeRecipeResponse2, CreateScheduleData, CreateScheduleResponse, DeleteScheduleData, DeleteScheduleResponse, ListSchedulesData, ListSchedulesResponse2, UpdateScheduleData, UpdateScheduleResponse, InspectRunningJobData, InspectRunningJobResponse, KillRunningJobData, PauseScheduleData, PauseScheduleResponse, RunNowHandlerData, RunNowHandlerResponse, SessionsHandlerData, SessionsHandlerResponse, UnpauseScheduleData, UnpauseScheduleResponse, ListSessionsData, ListSessionsResponse, GetSessionHistoryData, GetSessionHistoryResponse } from './types.gen';
|
||||
import type { AddSubRecipesData, AddSubRecipesResponse2, GetToolsData, GetToolsResponse, ReadAllConfigData, ReadAllConfigResponse, BackupConfigData, BackupConfigResponse, GetExtensionsData, GetExtensionsResponse, AddExtensionData, AddExtensionResponse, RemoveExtensionData, RemoveExtensionResponse, InitConfigData, InitConfigResponse, UpsertPermissionsData, UpsertPermissionsResponse, ProvidersData, ProvidersResponse2, ReadConfigData, RecoverConfigData, RecoverConfigResponse, RemoveConfigData, RemoveConfigResponse, UpsertConfigData, UpsertConfigResponse, ValidateConfigData, ValidateConfigResponse, ConfirmPermissionData, ManageContextData, ManageContextResponse, CreateRecipeData, CreateRecipeResponse2, DecodeRecipeData, DecodeRecipeResponse2, EncodeRecipeData, EncodeRecipeResponse2, CreateScheduleData, CreateScheduleResponse, DeleteScheduleData, DeleteScheduleResponse, ListSchedulesData, ListSchedulesResponse2, UpdateScheduleData, UpdateScheduleResponse, InspectRunningJobData, InspectRunningJobResponse, KillRunningJobData, PauseScheduleData, PauseScheduleResponse, RunNowHandlerData, RunNowHandlerResponse, SessionsHandlerData, SessionsHandlerResponse, UnpauseScheduleData, UnpauseScheduleResponse, ListSessionsData, ListSessionsResponse, GetSessionHistoryData, GetSessionHistoryResponse } from './types.gen';
|
||||
import { client as _heyApiClient } from './client.gen';
|
||||
|
||||
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = ClientOptions<TData, ThrowOnError> & {
|
||||
@@ -18,6 +18,17 @@ export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends
|
||||
meta?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export const addSubRecipes = <ThrowOnError extends boolean = false>(options: Options<AddSubRecipesData, ThrowOnError>) => {
|
||||
return (options.client ?? _heyApiClient).post<AddSubRecipesResponse2, unknown, ThrowOnError>({
|
||||
url: '/agent/add_sub_recipes',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const getTools = <ThrowOnError extends boolean = false>(options?: Options<GetToolsData, ThrowOnError>) => {
|
||||
return (options?.client ?? _heyApiClient).get<GetToolsResponse, unknown, ThrowOnError>({
|
||||
url: '/agent/tools',
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
|
||||
export type AddSubRecipesRequest = {
|
||||
sub_recipes: Array<SubRecipe>;
|
||||
};
|
||||
|
||||
export type AddSubRecipesResponse = {
|
||||
success: boolean;
|
||||
};
|
||||
|
||||
export type Annotations = {
|
||||
audience?: Array<Role>;
|
||||
priority?: number;
|
||||
@@ -777,6 +785,29 @@ export type UpsertPermissionsQuery = {
|
||||
tool_permissions: Array<ToolPermission>;
|
||||
};
|
||||
|
||||
export type AddSubRecipesData = {
|
||||
body: AddSubRecipesRequest;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/agent/add_sub_recipes';
|
||||
};
|
||||
|
||||
export type AddSubRecipesErrors = {
|
||||
/**
|
||||
* Unauthorized - invalid secret key
|
||||
*/
|
||||
401: unknown;
|
||||
};
|
||||
|
||||
export type AddSubRecipesResponses = {
|
||||
/**
|
||||
* added sub recipes to agent successfully
|
||||
*/
|
||||
200: AddSubRecipesResponse;
|
||||
};
|
||||
|
||||
export type AddSubRecipesResponse2 = AddSubRecipesResponses[keyof AddSubRecipesResponses];
|
||||
|
||||
export type GetToolsData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
|
||||
@@ -197,7 +197,6 @@ if (process.platform === 'win32') {
|
||||
const protocolUrl = commandLine.find((arg) => arg.startsWith('goose://'));
|
||||
if (protocolUrl) {
|
||||
const parsedUrl = new URL(protocolUrl);
|
||||
|
||||
// If it's a bot/recipe URL, handle it directly by creating a new window
|
||||
if (parsedUrl.hostname === 'bot' || parsedUrl.hostname === 'recipe') {
|
||||
app.whenReady().then(async () => {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { addSubRecipes, SubRecipe } from '../api';
|
||||
|
||||
export async function addSubRecipesToAgent(subRecipes: SubRecipe[]) {
|
||||
const add_sub_recipe_response = await addSubRecipes({ body: { sub_recipes: subRecipes } });
|
||||
if (add_sub_recipe_response.error) {
|
||||
console.warn(`Failed to add sub recipes: ${add_sub_recipe_response.error}`);
|
||||
} else {
|
||||
console.log('Added sub recipes');
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,13 @@ import { extractExtensionConfig } from '../components/settings/extensions/utils'
|
||||
import type { ExtensionConfig, FixedExtensionEntry } from '../components/ConfigContext';
|
||||
// TODO: remove when removing migration logic
|
||||
import { toastService } from '../toasts';
|
||||
import { ExtensionQuery, addExtension as apiAddExtension } from '../api';
|
||||
import {
|
||||
ExtensionQuery,
|
||||
RecipeParameter,
|
||||
SubRecipe,
|
||||
addExtension as apiAddExtension,
|
||||
} from '../api';
|
||||
import { addSubRecipesToAgent } from '../recipe/add_sub_recipe_on_agent';
|
||||
|
||||
export interface Provider {
|
||||
id: string; // Lowercase key (e.g., "openai")
|
||||
@@ -71,15 +77,19 @@ const substituteParameters = (text: string, params: Record<string, string>): str
|
||||
*/
|
||||
export const updateSystemPromptWithParameters = async (
|
||||
recipeParameters: Record<string, string>,
|
||||
recipeConfig?: { instructions?: string | null }
|
||||
recipeConfig?: {
|
||||
instructions?: string | null;
|
||||
sub_recipes?: SubRecipe[] | null;
|
||||
parameters?: RecipeParameter[] | null;
|
||||
}
|
||||
): Promise<void> => {
|
||||
const subRecipes = recipeConfig?.sub_recipes;
|
||||
try {
|
||||
const originalInstructions = recipeConfig?.instructions;
|
||||
|
||||
if (!originalInstructions) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Substitute parameters in the instructions
|
||||
const substitutedInstructions = substituteParameters(originalInstructions, recipeParameters);
|
||||
|
||||
@@ -103,6 +113,16 @@ export const updateSystemPromptWithParameters = async (
|
||||
} catch (error) {
|
||||
console.error('Error updating system prompt with parameters:', error);
|
||||
}
|
||||
if (subRecipes && subRecipes?.length > 0) {
|
||||
for (const subRecipe of subRecipes) {
|
||||
if (subRecipe.values) {
|
||||
for (const key in subRecipe.values) {
|
||||
subRecipe.values[key] = substituteParameters(subRecipe.values[key], recipeParameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
await addSubRecipesToAgent(subRecipes);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -192,9 +212,16 @@ export const initializeSystem = async (
|
||||
|
||||
// Get recipeConfig directly here
|
||||
const recipeConfig = window.appConfig?.get?.('recipe');
|
||||
const botPrompt = (recipeConfig as { instructions?: string })?.instructions;
|
||||
const recipe_instructions = (recipeConfig as { instructions?: string })?.instructions;
|
||||
const responseConfig = (recipeConfig as { response?: { json_schema?: unknown } })?.response;
|
||||
|
||||
const subRecipes = (recipeConfig as { sub_recipes?: SubRecipe[] })?.sub_recipes;
|
||||
const parameters = (recipeConfig as { parameters?: RecipeParameter[] })?.parameters;
|
||||
const hasParameters = parameters && parameters?.length > 0;
|
||||
const hasSubRecipes = subRecipes && subRecipes?.length > 0;
|
||||
let prompt = desktopPrompt;
|
||||
if (!hasParameters && recipe_instructions) {
|
||||
prompt = `${desktopPromptBot}\nIMPORTANT instructions for you to operate as agent:\n${recipe_instructions}`;
|
||||
}
|
||||
// Extend the system prompt with desktop-specific information
|
||||
const response = await fetch(getApiUrl('/agent/prompt'), {
|
||||
method: 'POST',
|
||||
@@ -203,21 +230,17 @@ export const initializeSystem = async (
|
||||
'X-Secret-Key': getSecretKey(),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
extension: botPrompt
|
||||
? `${desktopPromptBot}\nIMPORTANT instructions for you to operate as agent:\n${botPrompt}`
|
||||
: desktopPrompt,
|
||||
extension: prompt,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn(`Failed to extend system prompt: ${response.statusText}`);
|
||||
} else {
|
||||
console.log('Extended system prompt with desktop-specific information');
|
||||
if (botPrompt) {
|
||||
console.log('Added custom bot prompt to system prompt');
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasParameters && hasSubRecipes) {
|
||||
await addSubRecipesToAgent(subRecipes);
|
||||
}
|
||||
// Configure session with response config if present
|
||||
if (responseConfig?.json_schema) {
|
||||
const sessionConfigResponse = await fetch(getApiUrl('/agent/session_config'), {
|
||||
|
||||
Reference in New Issue
Block a user