Skip to content

Conversation

kasanatte
Copy link
Contributor

What type of PR is this?
/kind feature

What this PR does / why we need it:
This PR introduces the main chat handler and exposes the /api/v1/chat API endpoint. This handler serves as the core backend logic for the AI Assistant, orchestrating the MCP and OpenAI clients to:

  • Process user messages.
  • Handle tool calls to the MCP server.
  • Stream responses back to the frontend.

Which issue(s) this PR fixes:
Fixes #273

Special notes for your reviewer:
It depends on the changes from PR #275 & PR #276

Does this PR introduce a user-facing change?:

NONE

@karmada-bot karmada-bot added the kind/feature Categorizes issue or PR as related to a new feature. label Sep 26, 2025
@karmada-bot
Copy link
Collaborator

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign kevin-wangzefeng for approval. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Copy link

Summary of Changes

Hello @kasanatte, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request establishes the foundational API for an AI Assistant within the dashboard, exposing a new /api/v1/chat endpoint. This endpoint is designed to manage user interactions, leveraging both OpenAI for natural language processing and an MCP client for executing specific cluster management tools. It also includes a legacy /api/v1/assistant endpoint for basic chat functionality. The overall goal is to provide an intelligent interface for users to interact with and manage Karmada clusters.

Highlights

  • New Chat API Endpoint: Introduced the /api/v1/chat endpoint, serving as the core backend for the AI Assistant. This endpoint orchestrates message processing, tool calls, and streamed responses.
  • MCP and OpenAI Integration: The new chat handler seamlessly integrates both the Multi-Cluster Platform (MCP) and OpenAI clients, allowing the AI Assistant to process user messages, handle tool calls to the MCP server, and stream responses.
  • Legacy Assistant Endpoint: A /api/v1/assistant endpoint has been added to support basic, non-MCP chat functionality, primarily for direct OpenAI interactions.
  • Dynamic Tool Handling: The system now supports dynamic addition and execution of MCP tools, enabling the AI Assistant to retrieve real-time information from the Karmada cluster based on user queries.
  • Configuration Initialization: Initialization logic for both MCP and OpenAI client configurations has been added to the API startup process, ensuring proper setup of these services.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@karmada-bot karmada-bot added the size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. label Sep 26, 2025
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new chat API with streaming and tool-calling capabilities by integrating with OpenAI and a new MCP client. It also adds a legacy, simpler assistant endpoint. The overall structure is good, but there are several issues with error handling in the streaming logic that could lead to silent failures and a confusing experience for the client. Additionally, there's a bug in how streamed tool calls are accumulated, and some opportunities for code deduplication. My review provides specific suggestions to improve robustness and maintainability.

Comment on lines +155 to +161
if err != nil {
if errors.Is(err, io.EOF) {
break
}
klog.Errorf("Error receiving stream response: %v", err)
break
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

When resp.Recv() returns an error that is not io.EOF, the error is logged and the loop is broken, but the error is not propagated. The calling function handleChatCompletion then proceeds to process tool calls and send a completion signal, which is incorrect and misleading for the client.

To fix this, streamResponse should return the error. The caller, handleChatCompletion, should then check for this error, send an appropriate SSE error event to the client, and terminate the request instead of proceeding.

Comment on lines +230 to +233
if err := json.Unmarshal([]byte(toolCall.Function.Arguments), &args); err != nil {
klog.Errorf("Failed to parse tool arguments: %v, args: %s", err, toolCall.Function.Arguments)
return openai.ChatCompletionMessage{}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If parsing the tool arguments fails, an empty openai.ChatCompletionMessage is returned. This causes the tool call to be silently ignored by processToolCalls, and the LLM will not receive a result for its requested action. This can lead to confused or incomplete responses from the AI.

The error should be reported back to the LLM as the result of the tool call.

 	if err := json.Unmarshal([]byte(toolCall.Function.Arguments), &args); err != nil {
 		klog.Errorf("Failed to parse tool arguments: %v, args: %s", err, toolCall.Function.Arguments)
		result := fmt.Sprintf("Error parsing arguments for tool %s: %v", toolCall.Function.Name, err)
		return openai.ChatCompletionMessage{
			Role:       openai.ChatMessageRoleTool,
			Content:    result,
			Name:       toolCall.Function.Name,
			ToolCallID: toolCall.ID,
		}
	}

Comment on lines +285 to +287
if err != nil {
klog.Errorf("Failed to create final chat completion stream: %v", err)
return

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If creating the final chat completion stream fails, the error is logged and the function returns silently. The main handler then proceeds to send a completion signal, misleading the client into thinking the request was successful. The error should be propagated to the client via an SSE event.

 	if err != nil {
 		klog.Errorf("Failed to create final chat completion stream: %v", err)
		// It's important to notify the client about the error.
		msg := ChatResponse{Type: "error", Content: "Failed to get final response from LLM"}
		if data, err := json.Marshal(msg); err == nil {
			fmt.Fprintf(c.Writer, "data: %s\n\n", data)
			c.Writer.Flush()
		}
 		return
 	}

toolCallBuffer[index].Type = toolCall.Type
}
if toolCall.Function.Name != "" {
toolCallBuffer[index].Function.Name = toolCall.Function.Name

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The function name from a streamed tool call is being assigned (=) rather than appended (+=). According to the OpenAI API, the function name can also be delivered in chunks, similar to arguments. This implementation will fail to correctly assemble the function name if it is chunked, leading to failed tool calls.

 			toolCallBuffer[index].Function.Name += toolCall.Function.Name

Comment on lines +69 to +71
if err := session.run(); err != nil {
klog.Errorf("Answering session run failed: %v", err)
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Errors that occur within session.run() during the SSE stream (e.g., failure to create the stream or receive data) are logged but not communicated to the client. Since the HTTP headers have already been sent, the client connection will simply close without any indication of what went wrong. It's better to send an error event over the SSE stream to inform the client.

 	if err := session.run(); err != nil {
 		klog.Errorf("Answering session run failed: %v", err)
		session.sendStreamEvent("error", map[string]string{"message": err.Error()})
	}

Comment on lines +168 to +175
msg := ChatResponse{
Type: "content",
Content: choice.Delta.Content,
}
if data, err := json.Marshal(msg); err == nil {
fmt.Fprintf(c.Writer, "data: %s\n\n", data)
c.Writer.Flush()
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for marshaling a ChatResponse and sending it over the SSE stream is repeated in multiple places (here, processToolCalls, executeTool, etc.). This code duplication makes the code harder to maintain.

Consider extracting this logic into a dedicated helper function, for example sendEvent(c *gin.Context, response ChatResponse), and using it throughout the file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/feature Categorizes issue or PR as related to a new feature. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Umbrella][Summer OSPP 2025] Integrate Karmada-MCP-Server into the Karmada Dashboard

2 participants