Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,14 @@ run-ui-only:
@echo "Running UI..."
cd ui/desktop && npm install && npm run start-gui

debug-ui:
@echo "🚀 Starting goose frontend in external backend mode"
cd ui/desktop && \
export GOOSE_EXTERNAL_BACKEND=true && \
export GOOSE_EXTERNAL_PORT=3000 && \
npm install && \
npm run start-gui
debug-ui *alpha:
@echo "🚀 Starting goose frontend in external backend mode{{ if alpha == "alpha" { " with alpha features enabled" } else { "" } }}"
cd ui/desktop && \
export GOOSE_EXTERNAL_BACKEND=true && \
export GOOSE_EXTERNAL_PORT=3000 && \
{{ if alpha == "alpha" { "export ALPHA=true &&" } else { "" } }} \
npm install && \
npm run {{ if alpha == "alpha" { "start-alpha-gui" } else { "start-gui" } }}

# Run UI with main process debugging enabled
# To debug main process:
Expand Down
74 changes: 72 additions & 2 deletions crates/goose-server/src/routes/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use goose::config::PermissionManager;
use goose::config::Config;
use goose::model::ModelConfig;
use goose::prompt_template::render_global_file;
use goose::providers::create;
use goose::providers::{create, create_with_named_model};
use goose::recipe::Recipe;
use goose::recipe_deeplink;
use goose::session::{Session, SessionManager};
Expand All @@ -28,7 +28,7 @@ use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use tracing::error;
use tracing::{error, warn};

#[derive(Deserialize, utoipa::ToSchema)]
pub struct UpdateFromSessionRequest {
Expand Down Expand Up @@ -67,6 +67,7 @@ pub struct StartAgentRequest {
#[derive(Deserialize, utoipa::ToSchema)]
pub struct ResumeAgentRequest {
session_id: String,
load_model_and_extensions: bool,
}

#[utoipa::path(
Expand Down Expand Up @@ -172,6 +173,7 @@ async fn start_agent(
)
)]
async fn resume_agent(
State(state): State<Arc<AppState>>,
Json(payload): Json<ResumeAgentRequest>,
) -> Result<Json<Session>, ErrorResponse> {
let session = SessionManager::get_session(&payload.session_id, true)
Expand All @@ -184,6 +186,74 @@ async fn resume_agent(
}
})?;

if payload.load_model_and_extensions {
let agent = state
.get_agent_for_route(payload.session_id)
.await
.map_err(|code| ErrorResponse {
message: "Failed to get agent for route".into(),
status: code,
})?;

let config = Config::global();

let provider_result = async {
let provider_name: String =
config
.get_param("GOOSE_PROVIDER")
.map_err(|_| ErrorResponse {
message: "Could not configure agent: missing provider".into(),
status: StatusCode::INTERNAL_SERVER_ERROR,
})?;

let model: String = config.get_param("GOOSE_MODEL").map_err(|_| ErrorResponse {
message: "Could not configure agent: missing model".into(),
status: StatusCode::INTERNAL_SERVER_ERROR,
})?;

let provider = create_with_named_model(&provider_name, &model)
.await
.map_err(|_| ErrorResponse {
message: "Could not configure agent: missing model".into(),
status: StatusCode::INTERNAL_SERVER_ERROR,
})?;

agent
.update_provider(provider)
.await
.map_err(|e| ErrorResponse {
message: format!("Could not configure agent: {}", e),
status: StatusCode::INTERNAL_SERVER_ERROR,
})
};

let extensions_result = async {
let enabled_configs = goose::config::get_enabled_extensions();
let agent_clone = agent.clone();

let extension_futures = enabled_configs
.into_iter()
.map(|config| {
let config_clone = config.clone();
let agent_ref = agent_clone.clone();

async move {
if let Err(e) = agent_ref.add_extension(config_clone.clone()).await {
warn!("Failed to load extension {}: {}", config_clone.name(), e);
}
Ok::<_, ErrorResponse>(())
}
})
.collect::<Vec<_>>();

futures::future::join_all(extension_futures).await;
Ok::<(), ErrorResponse>(()) // Fixed type annotation
};

let (provider_result, _) = tokio::join!(provider_result, extensions_result);
provider_result?;
}

Ok(Json(session))
}

Expand Down
8 changes: 5 additions & 3 deletions crates/goose/src/session/session_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ pub struct SessionUpdateBuilder {
#[derive(Serialize, ToSchema, Debug)]
#[serde(rename_all = "camelCase")]
pub struct SessionInsights {
/// Total number of sessions
total_sessions: usize,
/// Total tokens used across all sessions
total_tokens: i64,
}

Expand Down Expand Up @@ -785,7 +783,9 @@ impl SessionStorage {
.await?;

let mut messages = Vec::new();
for (role_str, content_json, created_timestamp, metadata_json) in rows {
for (idx, (role_str, content_json, created_timestamp, metadata_json)) in
rows.into_iter().enumerate()
{
let role = match role_str.as_str() {
"user" => Role::User,
"assistant" => Role::Assistant,
Expand All @@ -799,6 +799,8 @@ impl SessionStorage {

let mut message = Message::new(role, created_timestamp, content);
message.metadata = metadata;
// TODO(Douwe): make id required
message = message.with_id(format!("msg_{}_{}", session_id, idx));
messages.push(message);
}

Expand Down
10 changes: 6 additions & 4 deletions ui/desktop/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -3794,9 +3794,13 @@
"ResumeAgentRequest": {
"type": "object",
"required": [
"session_id"
"session_id",
"load_model_and_extensions"
],
"properties": {
"load_model_and_extensions": {
"type": "boolean"
},
"session_id": {
"type": "string"
}
Expand Down Expand Up @@ -4122,13 +4126,11 @@
"properties": {
"totalSessions": {
"type": "integer",
"description": "Total number of sessions",
"minimum": 0
},
"totalTokens": {
"type": "integer",
"format": "int64",
"description": "Total tokens used across all sessions"
"format": "int64"
}
}
},
Expand Down
22 changes: 18 additions & 4 deletions ui/desktop/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,18 +89,32 @@ const PairRouteWrapper = ({
const setView = useNavigation();
const routeState =
(location.state as PairRouteState) || (window.history.state as PairRouteState) || {};
const [searchParams] = useSearchParams();
const [searchParams, setSearchParams] = useSearchParams();
const [initialMessage] = useState(routeState.initialMessage);

const resumeSessionId = searchParams.get('resumeSessionId') ?? undefined;

// Determine which session ID to use:
// 1. From route state (when navigating from Hub with a new session)
// 2. From URL params (when resuming a session)
// 3. From the existing chat state (when navigating to Pair directly)
const sessionId = routeState.resumeSessionId || resumeSessionId || chat.sessionId;

// Update URL with session ID if it's not already there (new chat from pair)
useEffect(() => {
if (process.env.ALPHA && sessionId && sessionId !== resumeSessionId) {
setSearchParams((prev) => {
prev.set('resumeSessionId', sessionId);
return prev;
});
}
}, [sessionId, resumeSessionId, setSearchParams]);

return process.env.ALPHA ? (
<Pair2
chat={chat}
setChat={setChat}
setView={setView}
setIsGoosehintsModalOpen={setIsGoosehintsModalOpen}
resumeSessionId={resumeSessionId}
sessionId={sessionId}
initialMessage={initialMessage}
/>
) : (
Expand Down
7 changes: 1 addition & 6 deletions ui/desktop/src/api/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,7 @@ export type Response = {
};

export type ResumeAgentRequest = {
load_model_and_extensions: boolean;
session_id: string;
};

Expand Down Expand Up @@ -707,13 +708,7 @@ export type SessionDisplayInfo = {
};

export type SessionInsights = {
/**
* Total number of sessions
*/
totalSessions: number;
/**
* Total tokens used across all sessions
*/
totalTokens: number;
};

Expand Down
37 changes: 0 additions & 37 deletions ui/desktop/src/components/AgentHeader.tsx

This file was deleted.

13 changes: 2 additions & 11 deletions ui/desktop/src/components/BaseChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
import React, { createContext, useContext, useEffect, useRef } from 'react';
import { useLocation } from 'react-router-dom';
import { SearchView } from './conversation/SearchView';
import { AgentHeader } from './AgentHeader';
import { RecipeHeader } from './RecipeHeader';
import LoadingGoose from './LoadingGoose';
import RecipeActivities from './recipes/RecipeActivities';
import PopularChatTopics from './PopularChatTopics';
Expand Down Expand Up @@ -315,16 +315,7 @@ function BaseChatContent({
{/* Recipe agent header - sticky at top of chat container */}
{recipe?.title && (
<div className="sticky top-0 z-10 bg-background-default px-0 -mx-6 mb-6 pt-6">
<AgentHeader
title={recipe.title}
profileInfo={
recipe.profile ? `${recipe.profile} - ${recipe.mcps || 12} MCPs` : undefined
}
onChangeProfile={() => {
console.log('Change profile clicked');
}}
showBorder={true}
/>
<RecipeHeader title={recipe.title} />
</div>
)}

Expand Down
Loading
Loading