-
Notifications
You must be signed in to change notification settings - Fork 473
Feat/idea tool provider integration #503
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| <!-- Defines IDEA IDE-specific contributions and implementations. --> | ||
| <idea-plugin> | ||
| </idea-plugin> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| <!-- Defines IDEA IDE-specific contributions and implementations. --> | ||
| <idea-plugin> | ||
| <extensions defaultExtensionNs="cc.unitmesh"> | ||
| <jsonTextProvider implementation="cc.unitmesh.devti.provider.local.LocalJsonTextProvider"/> | ||
| </extensions> | ||
| </idea-plugin> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| package cc.unitmesh.devins.idea.tool | ||
|
|
||
| import cc.unitmesh.agent.tool.ExecutableTool | ||
| import cc.unitmesh.devti.provider.toolchain.ToolchainFunctionProvider | ||
| import com.intellij.openapi.project.Project | ||
| import kotlinx.coroutines.runBlocking | ||
|
|
||
| /** | ||
| * ToolProvider implementation for IntelliJ IDEA. | ||
| * Collects all ToolchainFunctionProvider extensions and wraps them as ExecutableTools. | ||
| * | ||
| * This bridges the gap between IDEA's extension point system and mpp-core's tool registry, | ||
| * allowing IDEA-specific tools (like database, knowledge, component view, etc.) to be | ||
| * used by CodingAgent through the unified ToolOrchestrator. | ||
| * | ||
| * @param project The IntelliJ Project context required by ToolchainFunctionProvider | ||
| */ | ||
| class IdeaToolProvider(private val project: Project) { | ||
|
|
||
| /** | ||
| * Provide all IDEA tools from ToolchainFunctionProvider extensions. | ||
| * This method does not require ToolDependencies as IDEA tools use Project context instead. | ||
| */ | ||
| fun provideTools(): List<ExecutableTool<*, *>> { | ||
| val providers = ToolchainFunctionProvider.all() | ||
| val tools = mutableListOf<ExecutableTool<*, *>>() | ||
|
|
||
| for (provider in providers) { | ||
| try { | ||
| // Get tool infos from the provider | ||
| val toolInfos = runBlocking { provider.toolInfos(project) } | ||
|
|
||
| // Create an adapter for each tool | ||
| for (agentTool in toolInfos) { | ||
| val adapter = ToolchainFunctionAdapter( | ||
| provider = provider, | ||
| project = project, | ||
| agentTool = agentTool | ||
| ) | ||
| tools.add(adapter) | ||
| } | ||
|
|
||
| // If no toolInfos, try to create tools from funcNames | ||
| if (toolInfos.isEmpty()) { | ||
| val funcNames = runBlocking { provider.funcNames() } | ||
| for (funcName in funcNames) { | ||
| // Check if this provider is applicable for this function | ||
| val isApplicable = runBlocking { provider.isApplicable(project, funcName) } | ||
| if (isApplicable) { | ||
| val agentTool = cc.unitmesh.devti.agent.tool.AgentTool( | ||
| name = funcName, | ||
| description = "IDEA tool: $funcName", | ||
| example = "/$funcName" | ||
| ) | ||
| val adapter = ToolchainFunctionAdapter( | ||
| provider = provider, | ||
| project = project, | ||
| agentTool = agentTool | ||
| ) | ||
| tools.add(adapter) | ||
| } | ||
| } | ||
| } | ||
| } catch (e: Exception) { | ||
| // Log and continue with other providers | ||
| println("Warning: Failed to load tools from ${provider::class.simpleName}: ${e.message}") | ||
|
||
| } | ||
| } | ||
|
|
||
| return tools | ||
| } | ||
|
|
||
| companion object { | ||
| /** | ||
| * Create an IdeaToolProvider for the given project. | ||
| */ | ||
| fun create(project: Project): IdeaToolProvider { | ||
| return IdeaToolProvider(project) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| package cc.unitmesh.devins.idea.tool | ||
|
|
||
| import cc.unitmesh.agent.tool.* | ||
| import cc.unitmesh.agent.tool.schema.DeclarativeToolSchema | ||
| import cc.unitmesh.agent.tool.schema.SchemaProperty | ||
| import cc.unitmesh.agent.tool.schema.SchemaPropertyBuilder | ||
| import cc.unitmesh.agent.tool.schema.ToolCategory | ||
| import cc.unitmesh.devti.agent.tool.AgentTool | ||
| import cc.unitmesh.devti.provider.toolchain.ToolchainFunctionProvider | ||
| import com.intellij.openapi.project.Project | ||
|
|
||
| /** | ||
| * Parameters for ToolchainFunctionProvider execution. | ||
| * Uses a generic Map to support various parameter types from different providers. | ||
| */ | ||
| data class ToolchainFunctionParams( | ||
| val prop: String = "", | ||
| val args: List<Any> = emptyList(), | ||
| val allVariables: Map<String, Any?> = emptyMap() | ||
| ) | ||
|
|
||
| /** | ||
| * Schema for ToolchainFunctionProvider tools. | ||
| * Provides a generic schema that accepts prop, args, and variables. | ||
| */ | ||
| class ToolchainFunctionSchema( | ||
| private val toolDescription: String, | ||
| private val example: String = "" | ||
| ) : DeclarativeToolSchema( | ||
| description = toolDescription, | ||
| properties = mapOf( | ||
| "prop" to SchemaPropertyBuilder.string( | ||
| description = "Property or sub-command for the tool", | ||
| required = false | ||
| ), | ||
| "args" to SchemaProperty( | ||
| type = "array", | ||
| description = "Arguments to pass to the tool", | ||
| required = false, | ||
| items = SchemaProperty(type = "string", description = "Argument value") | ||
| ), | ||
| "allVariables" to SchemaProperty( | ||
| type = "object", | ||
| description = "Additional variables for the tool execution", | ||
| required = false, | ||
| additionalProperties = true | ||
| ) | ||
| ) | ||
| ) { | ||
| override fun getExampleUsage(toolName: String): String { | ||
| return if (example.isNotEmpty()) example else "/$toolName" | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Adapter that wraps a ToolchainFunctionProvider as an ExecutableTool. | ||
| * This allows IDEA-specific tools to be used in mpp-core's ToolOrchestrator. | ||
| * | ||
| * @param provider The ToolchainFunctionProvider to wrap | ||
| * @param project The IntelliJ Project context | ||
| * @param agentTool The AgentTool metadata from the provider | ||
| */ | ||
| class ToolchainFunctionAdapter( | ||
| private val provider: ToolchainFunctionProvider, | ||
| private val project: Project, | ||
| private val agentTool: AgentTool | ||
| ) : BaseExecutableTool<ToolchainFunctionParams, ToolResult>() { | ||
|
|
||
| override val name: String = agentTool.name | ||
| override val description: String = agentTool.description | ||
|
|
||
| override val metadata: ToolMetadata = ToolMetadata( | ||
| displayName = agentTool.name.replace("_", " ").replaceFirstChar { it.uppercase() }, | ||
| tuiEmoji = "🔧", | ||
| composeIcon = "extension", | ||
| category = ToolCategory.Utility, | ||
| schema = ToolchainFunctionSchema(agentTool.description, agentTool.example) | ||
| ) | ||
|
|
||
| override fun getParameterClass(): String = ToolchainFunctionParams::class.simpleName ?: "ToolchainFunctionParams" | ||
|
|
||
| override fun createToolInvocation(params: ToolchainFunctionParams): ToolInvocation<ToolchainFunctionParams, ToolResult> { | ||
| return ToolchainFunctionInvocation(params, this, provider, project, agentTool.name) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * ToolInvocation implementation for ToolchainFunctionProvider. | ||
| */ | ||
| class ToolchainFunctionInvocation( | ||
| override val params: ToolchainFunctionParams, | ||
| override val tool: ExecutableTool<ToolchainFunctionParams, ToolResult>, | ||
| private val provider: ToolchainFunctionProvider, | ||
| private val project: Project, | ||
| private val funcName: String | ||
| ) : ToolInvocation<ToolchainFunctionParams, ToolResult> { | ||
|
|
||
| override fun getDescription(): String { | ||
| return "Execute ${tool.name} with prop='${params.prop}'" | ||
| } | ||
|
|
||
| override fun getToolLocations(): List<ToolLocation> = emptyList() | ||
|
|
||
| override suspend fun execute(context: ToolExecutionContext): ToolResult { | ||
| return try { | ||
| val result = provider.execute( | ||
| project = project, | ||
| prop = params.prop, | ||
| args = params.args, | ||
| allVariables = params.allVariables, | ||
| commandName = funcName | ||
| ) | ||
|
|
||
| // Convert the result to ToolResult | ||
| when (result) { | ||
| is String -> ToolResult.Success(result) | ||
| is ToolResult -> result | ||
| else -> ToolResult.Success(result.toString()) | ||
| } | ||
| } catch (e: Exception) { | ||
| ToolResult.Error( | ||
| message = "Failed to execute ${tool.name}: ${e.message}", | ||
| errorType = "EXECUTION_ERROR", | ||
| metadata = mapOf("exception" to (e::class.simpleName ?: "Unknown")) | ||
| ) | ||
| } | ||
| } | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ import cc.unitmesh.devins.compiler.service.DevInsCompilerService | |
| import cc.unitmesh.devins.idea.compiler.IdeaDevInsCompilerService | ||
| import cc.unitmesh.devins.idea.renderer.JewelRenderer | ||
| import cc.unitmesh.devins.idea.services.IdeaToolConfigService | ||
| import cc.unitmesh.devins.idea.tool.IdeaToolProvider | ||
| import cc.unitmesh.devins.ui.config.AutoDevConfigWrapper | ||
| import cc.unitmesh.devins.ui.config.ConfigManager | ||
| import cc.unitmesh.llm.KoogLLMService | ||
|
|
@@ -261,6 +262,10 @@ class IdeaAgentViewModel( | |
| mcpToolConfigService = mcpToolConfigService, | ||
| enableLLMStreaming = true | ||
| ) | ||
|
|
||
| // Register IDEA-specific tools from ToolchainFunctionProvider extensions | ||
| registerIdeaTools(codingAgent!!) | ||
|
|
||
| agentInitialized = true | ||
|
|
||
| // Start observing PlanStateService and sync to renderer | ||
|
|
@@ -269,6 +274,27 @@ class IdeaAgentViewModel( | |
| return codingAgent!! | ||
| } | ||
|
|
||
| /** | ||
| * Register IDEA-specific tools from ToolchainFunctionProvider extensions. | ||
| * This bridges IDEA's extension point system with mpp-core's tool registry. | ||
| */ | ||
| private fun registerIdeaTools(agent: CodingAgent) { | ||
| try { | ||
| val ideaToolProvider = IdeaToolProvider.create(project) | ||
| val ideaTools = ideaToolProvider.provideTools() | ||
|
|
||
| for (tool in ideaTools) { | ||
| agent.registerTool(tool) | ||
| } | ||
|
|
||
| if (ideaTools.isNotEmpty()) { | ||
| println("Registered ${ideaTools.size} IDEA tools from ToolchainFunctionProvider extensions") | ||
|
||
| } | ||
| } catch (e: Exception) { | ||
| println("Warning: Failed to register IDEA tools: ${e.message}") | ||
|
||
| } | ||
| } | ||
|
|
||
| // Job for observing PlanStateService | ||
| private var planStateObserverJob: Job? = null | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid using
runBlockingin production code as it blocks the calling thread. Consider makingprovideTools()a suspend function or usingcoroutineScopewith appropriate dispatchers. If this method is called from UI code, consider using IntelliJ'sProgressManager.getInstance().run()orReadAction.nonBlocking()APIs to handle coroutines properly without blocking the thread.