[appkit plugin] [2/x] Add @databricks/appkit-agent package#384
[appkit plugin] [2/x] Add @databricks/appkit-agent package#384hubertzub-db wants to merge 3 commits intodatabricks:mainfrom
Conversation
There was a problem hiding this comment.
Looks pretty good (mostly just a copy-over of the code from databricks/appkit#166), but should we also include tests here/those seem missing in this PR?
bc6e379 to
3cb6507
Compare
@smurching agree, let's bring over tests in this PR. Initially I wanted to bring them over in latter parts but there's no guarantee we'll get there soon - so it sounds good to colocate them here. |
3cb6507 to
d4f6651
Compare
d4f6651 to
0ef5b7b
Compare
smurching
left a comment
There was a problem hiding this comment.
@hubertzub-db some findings from Claude Code review of the latest:
Gaps / Issues
- invoke-handler.ts:126 — chatHistory always includes the last message before the final user turn, not "all but last". If the input has
only one message (a single user turn), chatHistory is [] — that's fine. But if the last message is an assistant turn (e.g. [user,
assistant]), the assistant message gets passed as history and userMessages.at(-1) is the only user message. There's no test for an input
that ends with an assistant message — the handler returns 400 in that case actually (no user message check passes), but the chatHistory
still includes all items. Worth a test.- agent.ts — abortActiveOperations() has no test. The MCP client close path (both success and error) is untested.
- agent.ts — exports().invoke uses [...messages].reverse().find(m => m.role === "user") which picks the last user message, but
chatHistory is messages.slice(0, -1) which slices off the last message (not necessarily the last user message). For example, with [user,
user, assistant], invoke would pick the second user message correctly, but chatHistory would be [user, user] — the last assistant turn is
excluded. This looks like a bug, and there's no test for this case.- standard-agent.ts:178 — on_chat_model_stream with non-string content is silently skipped (e.g. array content from tool-use chunks).
There's no test verifying this doesn't leak unexpected events.- invoke-handler.test.ts — non-streaming agent error path is untested. The outer catch at line 155 (res.status(500)) is never exercised.
- hosted-tools.test.ts — resolveHostedTools([]) empty array case is missing, though trivially covered by the "resolves multiple" test
implicitly.The most significant issues are #3 (likely bug in exports().invoke history slicing) and #2 (no abortActiveOperations coverage). I'd recommend requesting fixes for those before merging.
I think the conclusions generally make sense, WDYT? Case #3 seems unlikely but could happen e.g. if the agent loop is interrupted, etc, and doesn't hurt to have a test for #2. But otherwise this looks good!
0ef5b7b to
d26b535
Compare
d26b535 to
71811c3
Compare
|
@smurching
|
b4927d9 to
a36b8f8
Compare
Move agent plugin source code into src/agent-plugin/ subfolder for better organization when hosting multiple plugins.
Signed-off-by: Hubert Zub <hubert.zub@databricks.com>
8c02856 to
c463ebf
Compare
🥞 Stacked PR
Summary
Extracts the agent plugin from the AppKit monorepo into a standalone, publishable package at
integrations/appkit-agent.This package provides:
AgentInterface— a contract for writing custom agent implementations that speak the OpenAI Responses API format (streaming + non-streaming).StandardAgent— a ready-to-use LangGraph-based ReAct agent with function tools and Databricks-hosted tool integration (Genie, Vector Search, MCP servers).Key decisions:
@databricks/appkitis a peer dependency — the plugin integrates via AppKit's publicPlugin/toPlugin/PluginManifestAPI@databricks/langchainjs,@langchain/core,@langchain/langgraph,@langchain/mcp-adapters) are optional peer dependencies — only needed when usingStandardAgent, not needed for customagentInstanceimplementationscreateLoggerreplaced with a local minimal console logger to avoid coupling to AppKit internalsappkit-agent.yml) and release workflow (release-appkit-agent.yml)