fix: strip provider prefix from all AI provider models and instant URL refresh
Backend fixes: - Strip provider prefix (anthropic:, openai:, deepseek:, ollama:) in all provider Chat methods and constructors for robust handling - Models are now correctly parsed regardless of caller format Frontend fixes: - Tool cards now persist in AI chat after approval execution by adding to streamEvents array - Dashboard now listens for pulse:metadata-changed custom event - AI Chat emits this event when set_resource_url tool completes - Guest URL icons now update instantly when AI sets them
This commit is contained in:
parent
43d658556b
commit
a3909f1b39
5 changed files with 58 additions and 4 deletions
|
|
@ -557,6 +557,14 @@ export const AIChat: Component<AIChatProps> = (props) => {
|
|||
};
|
||||
// Add to both toolCalls and streamEvents for chronological display
|
||||
const events = msg.streamEvents || [];
|
||||
|
||||
// Emit event for metadata refresh if AI set a resource URL
|
||||
if (data.name === 'set_resource_url' && data.success) {
|
||||
window.dispatchEvent(new CustomEvent('pulse:metadata-changed', {
|
||||
detail: { source: 'ai', tool: data.name }
|
||||
}));
|
||||
}
|
||||
|
||||
return {
|
||||
...msg,
|
||||
pendingTools: updatedPending,
|
||||
|
|
@ -748,11 +756,14 @@ export const AIChat: Component<AIChatProps> = (props) => {
|
|||
};
|
||||
|
||||
const remainingApprovals = m.pendingApprovals?.filter((a) => a.toolId !== approval.toolId) || [];
|
||||
const existingEvents = m.streamEvents || [];
|
||||
|
||||
return {
|
||||
...m,
|
||||
pendingApprovals: remainingApprovals,
|
||||
toolCalls: [...(m.toolCalls || []), newToolCall],
|
||||
// Also add to streamEvents so it appears in the chronological view
|
||||
streamEvents: [...existingEvents, { type: 'tool' as const, tool: newToolCall }],
|
||||
// Clear the stale "I need approval" content after the last approval is processed
|
||||
// The tool output will show the result instead
|
||||
content: remainingApprovals.length === 0 ? '' : m.content,
|
||||
|
|
|
|||
|
|
@ -290,15 +290,31 @@ export function Dashboard(props: DashboardProps) {
|
|||
// Total columns for colspan calculations
|
||||
const totalColumns = createMemo(() => visibleColumns().length);
|
||||
|
||||
// Load all guest metadata on mount (single API call for all guests)
|
||||
onMount(async () => {
|
||||
// Helper function to refresh guest metadata from server
|
||||
const refreshGuestMetadata = async () => {
|
||||
try {
|
||||
const metadata = await GuestMetadataAPI.getAllMetadata();
|
||||
updateGuestMetadataState(() => metadata || {});
|
||||
logger.debug('Guest metadata refreshed');
|
||||
} catch (err) {
|
||||
// Silently fail - metadata is optional for display
|
||||
logger.debug('Failed to load guest metadata', err);
|
||||
logger.debug('Failed to refresh guest metadata', err);
|
||||
}
|
||||
};
|
||||
|
||||
// Load all guest metadata on mount (single API call for all guests)
|
||||
onMount(async () => {
|
||||
await refreshGuestMetadata();
|
||||
});
|
||||
|
||||
// Listen for metadata changes from AI or other sources
|
||||
createEffect(() => {
|
||||
const handleMetadataChanged = () => {
|
||||
logger.debug('Metadata changed event received, refreshing...');
|
||||
refreshGuestMetadata();
|
||||
};
|
||||
|
||||
window.addEventListener('pulse:metadata-changed', handleMetadataChanged);
|
||||
return () => window.removeEventListener('pulse:metadata-changed', handleMetadataChanged);
|
||||
});
|
||||
|
||||
// Callback to update a guest's custom URL in metadata
|
||||
|
|
|
|||
|
|
@ -166,6 +166,10 @@ func (c *AnthropicClient) Chat(ctx context.Context, req ChatRequest) (*ChatRespo
|
|||
|
||||
// Use provided model or fall back to client default
|
||||
model := req.Model
|
||||
// Strip provider prefix if present - callers may pass the full "provider:model" string
|
||||
if len(model) > 10 && model[:10] == "anthropic:" {
|
||||
model = model[10:]
|
||||
}
|
||||
if model == "" {
|
||||
model = c.model
|
||||
}
|
||||
|
|
|
|||
|
|
@ -431,6 +431,10 @@ func (c *AnthropicOAuthClient) Chat(ctx context.Context, req ChatRequest) (*Chat
|
|||
}
|
||||
|
||||
model := req.Model
|
||||
// Strip provider prefix if present - callers may pass the full "provider:model" string
|
||||
if len(model) > 10 && model[:10] == "anthropic:" {
|
||||
model = model[10:]
|
||||
}
|
||||
if model == "" {
|
||||
model = c.model
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,11 +28,18 @@ type OpenAIClient struct {
|
|||
client *http.Client
|
||||
}
|
||||
|
||||
|
||||
// NewOpenAIClient creates a new OpenAI API client
|
||||
func NewOpenAIClient(apiKey, model, baseURL string) *OpenAIClient {
|
||||
if baseURL == "" {
|
||||
baseURL = openaiAPIURL
|
||||
}
|
||||
// Strip provider prefix if present - the model should be just the model name
|
||||
if strings.HasPrefix(model, "openai:") {
|
||||
model = strings.TrimPrefix(model, "openai:")
|
||||
} else if strings.HasPrefix(model, "deepseek:") {
|
||||
model = strings.TrimPrefix(model, "deepseek:")
|
||||
}
|
||||
return &OpenAIClient{
|
||||
apiKey: apiKey,
|
||||
model: model,
|
||||
|
|
@ -205,10 +212,19 @@ func (c *OpenAIClient) Chat(ctx context.Context, req ChatRequest) (*ChatResponse
|
|||
|
||||
// Use provided model or fall back to client default
|
||||
model := req.Model
|
||||
// Strip provider prefix if present - callers may pass the full "provider:model" string
|
||||
if strings.HasPrefix(model, "openai:") {
|
||||
model = strings.TrimPrefix(model, "openai:")
|
||||
} else if strings.HasPrefix(model, "deepseek:") {
|
||||
model = strings.TrimPrefix(model, "deepseek:")
|
||||
}
|
||||
if model == "" {
|
||||
model = c.model
|
||||
}
|
||||
|
||||
// Debug log to trace model issues
|
||||
log.Debug().Str("model", model).Str("req_model", req.Model).Str("c_model", c.model).Str("base_url", c.baseURL).Msg("OpenAI/DeepSeek Chat request")
|
||||
|
||||
// Build request
|
||||
openaiReq := openaiRequest{
|
||||
Model: model,
|
||||
|
|
@ -245,6 +261,9 @@ func (c *OpenAIClient) Chat(ctx context.Context, req ChatRequest) (*ChatResponse
|
|||
}
|
||||
}
|
||||
|
||||
// Log actual model being sent (INFO level for visibility)
|
||||
log.Info().Str("model_in_request", openaiReq.Model).Str("base_url", c.baseURL).Msg("Sending OpenAI/DeepSeek request")
|
||||
|
||||
body, err := json.Marshal(openaiReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal request: %w", err)
|
||||
|
|
|
|||
Loading…
Reference in a new issue