├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .github ├── ISSUE_TEMPLATE │ ├── 1-bug_report.yml │ ├── 2-feature_request.yml │ └── 3-documentation.yml ├── data │ └── agent-framework │ │ ├── .gitignore │ │ ├── 0.setup.sh │ │ ├── 1.run-scenarios.sh │ │ ├── README.md │ │ ├── package.json │ │ ├── resources │ │ ├── agent.yaml │ │ ├── model.yaml │ │ ├── tool-check.yaml │ │ └── tool-docs.template.yaml │ │ ├── run-challenge.sh │ │ └── scenario1 │ │ ├── README.md │ │ ├── configmap-missing-key.yaml │ │ ├── configmap-misspelled.yaml │ │ ├── configmap-readonly.yaml │ │ ├── deployment-env-mismatch.yaml │ │ ├── deployment-impossible-pod-affinity.yaml │ │ ├── deployment-low-resources.yaml │ │ ├── deployment-pod-affinity-wrong-key.yaml │ │ ├── deployment-probe-failures.yaml │ │ ├── deployment-scaled-down.yaml │ │ ├── missing-service-selector.yaml │ │ ├── network-policy.yaml │ │ ├── pod-hostport-conflict.yaml │ │ ├── pod-limit-range-exceeded.yaml │ │ ├── pod-resource-quota-exceeded.yaml │ │ ├── pod-security-context-issue.yaml │ │ ├── pvc-wrong-accessmode.yaml │ │ ├── pvc-wrong-storageclass.yaml │ │ ├── run.sh │ │ ├── secret-missing.yaml │ │ ├── secret-not-mounted.yaml │ │ ├── service-dns-resolution-fail.yaml │ │ ├── service-incorrect-port-number.yaml │ │ ├── service-no-endpoint.yaml │ │ ├── serviceaccount-misspelled.yaml │ │ ├── serviceaccount-permissions.yaml │ │ ├── test.js │ │ └── tests │ │ ├── chai-exec.js │ │ ├── chai-http.js │ │ ├── utils.js │ │ └── utils │ │ └── logging.js └── workflows │ ├── ci.yaml │ ├── image-scan.yaml │ ├── lint-python.yaml │ ├── run-agent-framework-test.yaml │ ├── tag.yaml │ ├── test.yaml │ └── tools_tests.yaml ├── .gitignore ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTION.md ├── DEVELOPMENT.md ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md ├── design └── template.md ├── go ├── .dockerignore ├── Dockerfile ├── Makefile ├── README.md ├── autogen │ ├── api │ │ ├── agents.go │ │ ├── context.go │ │ ├── memory.go │ │ ├── models.go │ │ ├── teams.go │ │ ├── terminations.go │ │ ├── tool_servers.go │ │ ├── tools.go │ │ └── types.go │ └── client │ │ ├── client.go │ │ ├── fake │ │ └── client.go │ │ ├── feedback.go │ │ ├── invoke.go │ │ ├── models.go │ │ ├── run.go │ │ ├── session.go │ │ ├── team.go │ │ ├── tools.go │ │ ├── toolservers.go │ │ ├── types.go │ │ ├── types_test.go │ │ └── validate.go ├── cli │ ├── Makefile │ ├── cmd │ │ └── kagent │ │ │ └── main.go │ └── internal │ │ ├── cli │ │ ├── a2a.go │ │ ├── bug_report.go │ │ ├── chat.go │ │ ├── const.go │ │ ├── const_test.go │ │ ├── create.go │ │ ├── dashboard.go │ │ ├── dashboard_darwin.go │ │ ├── delete.go │ │ ├── format.go │ │ ├── get.go │ │ ├── install.go │ │ ├── invoke.go │ │ ├── utils.go │ │ └── version.go │ │ └── config │ │ ├── config.go │ │ ├── utils.go │ │ └── utils_test.go ├── config │ ├── crd │ │ └── bases │ │ │ ├── kagent.dev_agents.yaml │ │ │ ├── kagent.dev_memories.yaml │ │ │ ├── kagent.dev_modelconfigs.yaml │ │ │ ├── kagent.dev_teams.yaml │ │ │ └── kagent.dev_toolservers.yaml │ └── rbac │ │ └── role.yaml ├── controller │ ├── .dockerignore │ ├── .gitignore │ ├── PROJECT │ ├── README.md │ ├── api │ │ └── v1alpha1 │ │ │ ├── autogenagent_types.go │ │ │ ├── autogenmemory_types.go │ │ │ ├── autogenmodelconfig_types.go │ │ │ ├── autogenteam_types.go │ │ │ ├── groupversion_info.go │ │ │ ├── toolserver_types.go │ │ │ └── zz_generated.deepcopy.go │ ├── cmd │ │ ├── main.go │ │ └── main_test.go │ ├── hack │ │ └── boilerplate.go.txt │ ├── internal │ │ ├── a2a │ │ │ ├── a2a_handler_mux.go │ │ │ ├── a2a_reconciler.go │ │ │ ├── a2a_task_processor.go │ │ │ ├── autogen_a2a_translator.go │ │ │ └── autogen_a2a_translator_test.go │ │ ├── autogen │ │ │ ├── autogen_api_translator.go │ │ │ ├── autogen_builtin_planning_agent.go │ │ │ ├── autogen_reconciler.go │ │ │ ├── autogen_translator_golden_test.go │ │ │ ├── autogen_translator_test.go │ │ │ ├── planning-agent-system-prompt.txt │ │ │ └── testdata │ │ │ │ ├── README.md │ │ │ │ ├── inputs │ │ │ │ ├── agent_with_builtin_tools.yaml │ │ │ │ ├── agent_with_memory.yaml │ │ │ │ ├── agent_with_nested_agent.yaml │ │ │ │ ├── anthropic_agent.yaml │ │ │ │ ├── basic_agent.yaml │ │ │ │ └── ollama_agent.yaml │ │ │ │ └── outputs │ │ │ │ ├── agent_with_builtin_tools.json │ │ │ │ ├── agent_with_memory.json │ │ │ │ ├── agent_with_nested_agent.json │ │ │ │ ├── anthropic_agent.json │ │ │ │ ├── basic_agent.json │ │ │ │ └── ollama_agent.json │ │ ├── client_wrapper │ │ │ ├── client_wrapper.go │ │ │ └── client_wrapper_test.go │ │ ├── controller │ │ │ ├── autogenagent_controller.go │ │ │ ├── autogenmemory_controller.go │ │ │ ├── autogenmodelconfig_controller.go │ │ │ ├── autogensecret_controller.go │ │ │ ├── autogenteam_controller.go │ │ │ └── toolserver_controller.go │ │ ├── httpserver │ │ │ ├── errors │ │ │ │ └── errors.go │ │ │ ├── handlers │ │ │ │ ├── feedback.go │ │ │ │ ├── handlers.go │ │ │ │ ├── health.go │ │ │ │ ├── helpers.go │ │ │ │ ├── invoke.go │ │ │ │ ├── invoke_test.go │ │ │ │ ├── memory.go │ │ │ │ ├── mock_client_test.go │ │ │ │ ├── modelconfig.go │ │ │ │ ├── models.go │ │ │ │ ├── providers.go │ │ │ │ ├── sessions.go │ │ │ │ ├── teams.go │ │ │ │ ├── tools.go │ │ │ │ └── toolservers.go │ │ │ ├── helpers.go │ │ │ ├── middleware.go │ │ │ ├── middleware_error.go │ │ │ └── server.go │ │ └── utils │ │ │ ├── common.go │ │ │ └── syncutils │ │ │ └── sync_utils.go │ └── utils │ │ └── a2autils │ │ └── a2a_utils.go ├── go.mod ├── go.sum └── test │ └── e2e │ ├── e2e_test.go │ ├── invoke_api_test.go │ ├── manifests │ └── gpt-model-config.yaml │ └── systemprompts │ ├── kube-expert-system-prompt.txt │ ├── kubectl-user-system-prompt.txt │ ├── planning-agent-system-prompt.txt │ └── prometheus-expert-system-prompt.txt ├── helm ├── .gitignore ├── README-testing.md ├── README.md ├── agents │ ├── argo-rollouts │ │ ├── Chart-template.yaml │ │ ├── templates │ │ │ ├── agent.yaml │ │ │ └── rbac.yaml │ │ └── values.yaml │ ├── cilium-crd │ │ ├── Chart-template.yaml │ │ ├── templates │ │ │ ├── agent.yaml │ │ │ └── rbac.yaml │ │ └── values.yaml │ ├── helm │ │ ├── Chart-template.yaml │ │ ├── templates │ │ │ └── agent.yaml │ │ └── values.yaml │ ├── istio │ │ ├── Chart-template.yaml │ │ ├── templates │ │ │ ├── agent.yaml │ │ │ └── rbac.yaml │ │ └── values.yaml │ ├── k8s │ │ ├── Chart-template.yaml │ │ ├── templates │ │ │ └── agent.yaml │ │ └── values.yaml │ ├── kgateway │ │ ├── Chart-template.yaml │ │ ├── templates │ │ │ └── agent.yaml │ │ └── values.yaml │ ├── observability │ │ ├── Chart-template.yaml │ │ ├── templates │ │ │ └── agent.yaml │ │ └── values.yaml │ └── promql │ │ ├── Chart-template.yaml │ │ ├── templates │ │ └── agent.yaml │ │ └── values.yaml ├── kagent-crds │ ├── Chart-template.yaml │ ├── templates │ │ ├── kagent.dev_agents.yaml │ │ ├── kagent.dev_memories.yaml │ │ ├── kagent.dev_modelconfigs.yaml │ │ ├── kagent.dev_teams.yaml │ │ └── kagent.dev_toolservers.yaml │ └── values.yaml └── kagent │ ├── Chart-template.yaml │ ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── clusterrole.yaml │ ├── clusterrolebinding.yaml │ ├── deployment.yaml │ ├── modelconfig.yaml │ ├── secret.yaml │ ├── service.yaml │ └── serviceaccount.yaml │ ├── tests │ ├── deployment_test.yaml │ ├── integration_test.yaml │ ├── modelconfig_test.yaml │ ├── rbac_test.yaml │ ├── secret_test.yaml │ └── service_test.yaml │ └── values.yaml ├── img ├── arch.png ├── hero.png ├── icon-dark.svg └── icon-light.svg ├── python ├── .dockerignore ├── .env.example ├── .gitignore ├── .python-version ├── Dockerfile ├── Makefile ├── README.md ├── pyproject.toml ├── src │ ├── autogenstudio │ │ ├── __init__.py │ │ ├── cli.py │ │ ├── database │ │ │ ├── __init__.py │ │ │ ├── db_manager.py │ │ │ └── schema_manager.py │ │ ├── datamodel │ │ │ ├── __init__.py │ │ │ ├── db.py │ │ │ ├── eval.py │ │ │ └── types.py │ │ ├── eval │ │ │ ├── __init__.py │ │ │ ├── judges.py │ │ │ ├── orchestrator.py │ │ │ └── runners.py │ │ ├── gallery │ │ │ ├── __init__.py │ │ │ ├── builder.py │ │ │ └── tools │ │ │ │ ├── __init__.py │ │ │ │ ├── bing_search.py │ │ │ │ ├── calculator.py │ │ │ │ ├── fetch_webpage.py │ │ │ │ ├── generate_image.py │ │ │ │ └── google_search.py │ │ ├── sessionmanager │ │ │ ├── __init__.py │ │ │ └── sessionmanager.py │ │ ├── teammanager │ │ │ ├── __init__.py │ │ │ └── teammanager.py │ │ ├── toolmanager │ │ │ ├── __init__.py │ │ │ └── toolmanager.py │ │ ├── toolservermanager │ │ │ ├── __init__.py │ │ │ └── toolserver_manager.py │ │ ├── utils │ │ │ ├── __init__.py │ │ │ └── utils.py │ │ ├── validation │ │ │ ├── __init__.py │ │ │ ├── component_test_service.py │ │ │ └── validation_service.py │ │ ├── version.py │ │ └── web │ │ │ ├── __init__.py │ │ │ ├── app.py │ │ │ ├── auth │ │ │ ├── __init__.py │ │ │ ├── authroutes.py │ │ │ ├── dependencies.py │ │ │ ├── exceptions.py │ │ │ ├── manager.py │ │ │ ├── middleware.py │ │ │ ├── models.py │ │ │ ├── providers.py │ │ │ └── wsauth.py │ │ │ ├── config.py │ │ │ ├── deps.py │ │ │ ├── initialization.py │ │ │ ├── managers │ │ │ ├── __init__.py │ │ │ ├── connection.py │ │ │ └── run_context.py │ │ │ └── routes │ │ │ ├── __init__.py │ │ │ ├── feedback.py │ │ │ ├── gallery.py │ │ │ ├── invoke.py │ │ │ ├── models.py │ │ │ ├── runs.py │ │ │ ├── sessions.py │ │ │ ├── settingsroute.py │ │ │ ├── teams.py │ │ │ ├── tool_servers.py │ │ │ ├── tools.py │ │ │ ├── validation.py │ │ │ └── ws.py │ └── kagent │ │ ├── __init__.py │ │ ├── agents │ │ ├── __init__.py │ │ └── _task_agent.py │ │ ├── cli.py │ │ ├── memory │ │ ├── __init__.py │ │ └── _pinecone_memory.py │ │ ├── models │ │ └── vertexai │ │ │ ├── __init__.py │ │ │ ├── _anthropic_vertex_client.py │ │ │ ├── _gemini_vertexai_client.py │ │ │ ├── _model_info.py │ │ │ └── config │ │ │ └── __init__.py │ │ ├── py.typed │ │ ├── tool_servers │ │ ├── __init__.py │ │ ├── _ssemcptoolserver.py │ │ ├── _stdiomcptoolserver.py │ │ └── _tool_server.py │ │ └── tools │ │ ├── __init__.py │ │ ├── _utils.py │ │ ├── argo │ │ ├── __init__.py │ │ ├── _argo_rollouts_k8sgw_installation.py │ │ └── _kubectl_argo_rollouts.py │ │ ├── cilium │ │ ├── __init__.py │ │ ├── _cilium.py │ │ └── _cilium_dbg.py │ │ ├── common │ │ ├── __init__.py │ │ ├── _llm_tool.py │ │ └── _shell.py │ │ ├── datetime │ │ ├── __init__.py │ │ └── _current_date_time.py │ │ ├── docs │ │ ├── __init__.py │ │ └── query_documentation.py │ │ ├── grafana │ │ ├── __init__.py │ │ └── _grafana.py │ │ ├── helm │ │ ├── __init__.py │ │ └── _helm.py │ │ ├── istio │ │ ├── __init__.py │ │ └── _istioctl.py │ │ ├── k8s │ │ ├── __init__.py │ │ ├── _generate_resource.py │ │ ├── _kubectl.py │ │ ├── _models.py │ │ ├── _prompt_registry.py │ │ ├── _resource_types.py │ │ ├── argo │ │ │ ├── __init__.py │ │ │ ├── _analysis_template.py │ │ │ └── _rollout.py │ │ ├── gateway_api │ │ │ ├── __init__.py │ │ │ ├── _gateway.py │ │ │ ├── _gateway_class.py │ │ │ ├── _grpc_route.py │ │ │ ├── _http_route.py │ │ │ └── _reference_grant.py │ │ └── istio │ │ │ ├── __init__.py │ │ │ ├── _auth_policy.py │ │ │ ├── _gateway.py │ │ │ ├── _peer_auth.py │ │ │ └── _virtual_service.py │ │ ├── prometheus │ │ ├── __init__.py │ │ ├── _prometheus.py │ │ └── _promql.py │ │ └── utils │ │ └── tool_gen.py ├── tests │ ├── __init__.py │ ├── _test_result.py │ ├── _yaml_comparer.py │ ├── test_generate_resource.py │ ├── test_load_agents.py │ ├── test_yaml_comparer.py │ └── testcases │ │ └── auth_policy_tests.yaml └── uv.lock ├── scripts └── get-kagent └── ui ├── .dockerignore ├── .gitignore ├── .nvmrc ├── Dockerfile ├── Makefile ├── README.md ├── bunfig.toml ├── components.json ├── conf ├── nginx.conf └── supervisord.conf ├── cypress.config.ts ├── cypress └── e2e │ └── smoke.cy.ts ├── eslint.config.mjs ├── jest.config.ts ├── jest.setup.ts ├── next.config.ts ├── package-lock.json ├── package.json ├── postcss.config.mjs ├── src ├── app │ ├── actions │ │ ├── feedback.ts │ │ ├── memories.ts │ │ ├── modelConfigs.ts │ │ ├── models.ts │ │ ├── providers.ts │ │ ├── servers.ts │ │ ├── sessions.ts │ │ ├── teams.ts │ │ ├── tools.ts │ │ └── utils.ts │ ├── agents │ │ ├── [agentId] │ │ │ └── chat │ │ │ │ ├── [chatId] │ │ │ │ └── page.tsx │ │ │ │ ├── layout.tsx │ │ │ │ └── page.tsx │ │ ├── new │ │ │ └── page.tsx │ │ └── page.tsx │ ├── globals.css │ ├── icon.tsx │ ├── layout.tsx │ ├── loading.tsx │ ├── memories │ │ ├── new │ │ │ └── page.tsx │ │ └── page.tsx │ ├── models │ │ ├── new │ │ │ └── page.tsx │ │ └── page.tsx │ ├── page.tsx │ ├── servers │ │ └── page.tsx │ ├── stream │ │ └── [sessionId] │ │ │ └── route.ts │ └── tools │ │ └── page.tsx ├── components │ ├── AddServerDialog.tsx │ ├── AgentCard.tsx │ ├── AgentGrid.tsx │ ├── AgentList.tsx │ ├── AgentsProvider.tsx │ ├── AppInitializer.tsx │ ├── ConfirmDialog.tsx │ ├── DeleteAgentButton.tsx │ ├── ErrorState.tsx │ ├── Footer.tsx │ ├── Header.tsx │ ├── LoadingState.tsx │ ├── ModelProviderCombobox.tsx │ ├── SettingsModal.tsx │ ├── ThemeProvider.tsx │ ├── ThemeToggle.tsx │ ├── ToolDisplay.tsx │ ├── chat │ │ ├── ChatInterface.tsx │ │ ├── ChatLayoutUI.tsx │ │ ├── ChatMessage.tsx │ │ ├── CodeBlock.tsx │ │ ├── FeedbackDialog.tsx │ │ ├── LLMCallModal.tsx │ │ ├── MemoryQueryDisplay.tsx │ │ ├── NoMessagesState.tsx │ │ ├── StatusDisplay.tsx │ │ ├── StreamingMessage.tsx │ │ ├── TokenStats.tsx │ │ ├── ToolCallDisplay.tsx │ │ └── TruncatableText.tsx │ ├── create │ │ ├── MemorySelectionSection.tsx │ │ ├── ModelSelectionSection.tsx │ │ ├── ProviderFilter.tsx │ │ ├── SelectToolsDialog.tsx │ │ ├── SystemPromptSection.tsx │ │ └── ToolsSection.tsx │ ├── icons │ │ ├── Anthropic.tsx │ │ ├── Azure.tsx │ │ ├── McpIcon.tsx │ │ ├── Ollama.tsx │ │ ├── OpenAI.tsx │ │ └── Twitter.tsx │ ├── kagent-logo-text.tsx │ ├── kagent-logo.tsx │ ├── models │ │ └── new │ │ │ ├── AuthSection.tsx │ │ │ ├── BasicInfoSection.tsx │ │ │ └── ParamsSection.tsx │ ├── onboarding │ │ ├── OnboardingWizard.tsx │ │ └── steps │ │ │ ├── AgentSetupStep.tsx │ │ │ ├── FinishStep.tsx │ │ │ ├── ModelConfigStep.tsx │ │ │ ├── ReviewStep.tsx │ │ │ ├── ToolSelectionStep.tsx │ │ │ └── WelcomeStep.tsx │ ├── sidebars │ │ ├── AgentActions.tsx │ │ ├── AgentDetailsSidebar.tsx │ │ ├── AgentSwitcher.tsx │ │ ├── ChatItem.tsx │ │ ├── EmptyState.tsx │ │ ├── GroupedChats.tsx │ │ ├── SessionGroup.tsx │ │ └── SessionsSidebar.tsx │ ├── tools │ │ └── CategoryFilter.tsx │ └── ui │ │ ├── alert-dialog.tsx │ │ ├── alert.tsx │ │ ├── badge.tsx │ │ ├── button.tsx │ │ ├── card.tsx │ │ ├── checkbox.tsx │ │ ├── collapsible.tsx │ │ ├── command.tsx │ │ ├── dialog.tsx │ │ ├── dropdown-menu.tsx │ │ ├── form.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── popover.tsx │ │ ├── progress.tsx │ │ ├── radio-group.tsx │ │ ├── scroll-area.tsx │ │ ├── select.tsx │ │ ├── separator.tsx │ │ ├── sheet.tsx │ │ ├── sidebar.tsx │ │ ├── skeleton.tsx │ │ ├── sonner.tsx │ │ ├── table.tsx │ │ ├── tabs.tsx │ │ ├── textarea.tsx │ │ └── tooltip.tsx ├── hooks │ └── use-mobile.tsx ├── lib │ ├── __tests__ │ │ ├── providers.test.ts │ │ ├── toolUtils.test.ts │ │ └── utils.test.ts │ ├── constants.ts │ ├── messageHandlers.ts │ ├── providers.ts │ ├── toolUtils.ts │ ├── types.ts │ ├── userStore.ts │ └── utils.ts └── types │ └── datamodel.ts ├── tailwind.config.ts └── tsconfig.json /.github/ISSUE_TEMPLATE/3-documentation.yml: -------------------------------------------------------------------------------- 1 | name: 📚 Documentation Request 2 | description: Report a documentation bug, missing info, or suggest an improvement 3 | title: "[DOCS] " 4 | labels: [] 5 | type: Documentation 6 | assignees: [] 7 | 8 | body: 9 | - type: checkboxes 10 | id: prerequisites 11 | attributes: 12 | label: 📋 Prerequisites 13 | description: Please check these boxes before submitting your documentation issue 14 | options: 15 | - label: I have searched the [existing issues](./issues) to avoid creating a duplicate 16 | required: true 17 | - label: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/kagent-dev/kagent/blob/main/CODE_OF_CONDUCT.md) 18 | required: true 19 | 20 | - type: textarea 21 | id: description 22 | attributes: 23 | label: 📝 Issue Description 24 | description: Clearly describe the documentation problem, gap, or improvement suggestion 25 | placeholder: | 26 | - What is missing, unclear, or incorrect? 27 | - What would you like to see improved or added? 28 | validations: 29 | required: true 30 | 31 | - type: textarea 32 | id: expected_content 33 | attributes: 34 | label: ✅ Suggested Content or Change 35 | description: What should the improved documentation include? (You can paste sample text, suggestions, diagrams, etc.) 36 | 37 | - type: checkboxes 38 | id: contribution 39 | attributes: 40 | label: 🙋 Willing to Contribute? 41 | description: Let us know if you’d like to submit a pull request with the documentation fix 42 | options: 43 | - label: I am willing to submit a PR for this documentation update 44 | -------------------------------------------------------------------------------- /.github/data/agent-framework/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.failure 3 | *.success -------------------------------------------------------------------------------- /.github/data/agent-framework/0.setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR=$(cd $(dirname ${BASH_SOURCE[0]}); pwd) 4 | 5 | # Make sure envsubst is available 6 | if ! command -v envsubst &> /dev/null; then 7 | echo "Installing gettext package for envsubst..." 8 | 9 | # Detect the operating system for installing the right package 10 | if [ "$(uname)" == "Darwin" ]; then 11 | # macOS 12 | brew install gettext 13 | brew link --force gettext 14 | elif [ -f /etc/debian_version ]; then 15 | # Debian/Ubuntu 16 | sudo apt-get update 17 | sudo apt-get install -y gettext 18 | elif [ -f /etc/redhat-release ]; then 19 | # RHEL/CentOS/Fedora 20 | sudo yum install -y gettext 21 | else 22 | echo "Unsupported OS. Please install gettext package manually." 23 | exit 1 24 | fi 25 | fi 26 | 27 | # Check if required environment variables are set 28 | if [ -z "${OPENAI_API_KEY}" ] || [ -z "${QDRANT_API_KEY}" ]; then 29 | echo "Error: Required environment variables are not set. Please set them before running this script." 30 | echo "Example:" 31 | echo "export OPENAI_API_KEY=\"your-openai-api-key\"" 32 | echo "export QDRANT_API_KEY=\"your-qdrant-api-key\"" 33 | exit 1 34 | fi 35 | 36 | make build-all 37 | make create-kind-cluster 38 | 39 | make build-cli-local 40 | sudo mv go/bin/kagent-local /usr/local/bin/kagent 41 | make kind-load-docker-images 42 | make helm-install 43 | 44 | kubectl apply -f "${SCRIPT_DIR}/resources/agent.yaml" 45 | kubectl apply -f "${SCRIPT_DIR}/resources/tool-check.yaml" 46 | kubectl apply -f "${SCRIPT_DIR}/resources/model.yaml" 47 | 48 | # Use environment variable substitution to create the final YAML and apply it 49 | envsubst < "${SCRIPT_DIR}/resources/tool-docs.template.yaml" | kubectl apply -f - 50 | -------------------------------------------------------------------------------- /.github/data/agent-framework/1.run-scenarios.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | # Define a function to log messages with timestamp 5 | log() { 6 | echo "[$(date +'%Y-%m-%dT%H:%M:%S')] $1" 7 | } 8 | 9 | export CLUSTER_CTX=kind-kagent 10 | # Loop through each challenge defined in the .github/data/agent-framework directory 11 | for scenario_dir in scenario*; do 12 | if [ ! -d "$scenario_dir" ]; then 13 | continue 14 | fi 15 | 16 | npm i || pnpm i 17 | echo "pwd=$(pwd)" 18 | for challenge_path in ${scenario_dir}/*.yaml; do 19 | challenge_file=$(basename "$challenge_path") 20 | # reset environment 21 | bash "./${scenario_dir}/run.sh" 22 | bash ./run-challenge.sh "$scenario_dir" "$challenge_file" 23 | kubectl --context "${CLUSTER_CTX}" delete deploy --all -n default 24 | done 25 | done 26 | -------------------------------------------------------------------------------- /.github/data/agent-framework/README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes Agent Benchmark 2 | 3 | 4 | 1. From the root of the repository, run the command below. You can make it faster by setting your architecture to `amd64` or `arm64`: 5 | 6 | ```bash 7 | export BUILD_ARGS="--platform linux/amd64" 8 | bash .github/data/agent-framework/0.setup.sh 9 | ``` 10 | 11 | Validate that the `kagent` cli is setup and the cluster is running: 12 | 13 | ```bash 14 | kagent version 15 | kubectl get pods -A 16 | ``` 17 | 18 | 2. **Run individual challenges** by navigating to the `.github/data/agent-framework` running the following command: 19 | 20 | ```bash 21 | export CLUSTER_CTX=kind-kagent 22 | cd .github/data/agent-framework 23 | scenario1/run.sh 24 | npm i 25 | npm i -g mocha 26 | 27 | # ../run-challenge.sh scenario1 28 | ./run-challenge.sh scenario1 deployment-probe-failures.yaml 29 | ``` 30 | 31 | or 32 | 33 | 2. Run all challenges at once: 34 | 35 | ```bash 36 | ./1.run-scenarios.sh 37 | ``` 38 | -------------------------------------------------------------------------------- /.github/data/agent-framework/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "procgen", 3 | "version": "0.0.1", 4 | "description": "Solo Procedure Generator", 5 | "main": "procgen.js", 6 | "dependencies": { 7 | "@jsdevtools/chai-exec": "^2.1.1", 8 | "chai": "^5.1.2", 9 | "deep-object-diff": "^1.1.9", 10 | "js-yaml": "^4.1.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.github/data/agent-framework/resources/model.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kagent.dev/v1alpha1 2 | kind: ModelConfig 3 | metadata: 4 | name: default-model-config 5 | namespace: kagent 6 | spec: 7 | apiKeySecretKey: OPENAI_API_KEY 8 | apiKeySecretRef: kagent-openai 9 | model: o4-mini-2025-04-16 10 | provider: OpenAI 11 | -------------------------------------------------------------------------------- /.github/data/agent-framework/resources/tool-check.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kagent.dev/v1alpha1 2 | kind: ToolServer 3 | metadata: 4 | name: check-kubernetes-cluster-fixed 5 | namespace: kagent 6 | spec: 7 | config: 8 | stdio: 9 | args: 10 | - check-kubernetes-cluster-fixed@0.0.7 11 | command: npx 12 | env: 13 | CONTEXT: kind-kagent 14 | description: Check Kubernetes Cluster Fixed 15 | -------------------------------------------------------------------------------- /.github/data/agent-framework/resources/tool-docs.template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kagent.dev/v1alpha1 2 | kind: ToolServer 3 | metadata: 4 | name: search-documentation 5 | namespace: kagent 6 | spec: 7 | config: 8 | stdio: 9 | args: 10 | - qdrant-search-mcp-server 11 | - --collections="istio,gloo-mesh-enterprise,ambient,argo-rollouts,cilium,gateway-api,github-istio,github-solo-reference-architectures,gloo-edge,gloo-mesh-core,helm,kgateway,kubernetes,mcp,otel,prometheus,gloo-gateway" 12 | - --name=searchDocs 13 | - '--description="Search documentation for the following products: Istio, Gloo 14 | Mesh Enterprise, Ambient, Argo Rollouts, Cilium, Gateway API, GitHub Istio 15 | Issues, GitHub Solo Reference Architectures, Gloo Edge, Gloo Mesh Core, Helm, 16 | KGateway, Kubernetes, MCP, OpenTelemetry, Prometheus, Gloo Gateway"' 17 | command: npx 18 | env: 19 | OPENAI_API_KEY: ${OPENAI_API_KEY} 20 | QDRANT_API_KEY: ${QDRANT_API_KEY} 21 | QDRANT_URL: https://qdrant.is.solo.io 22 | description: Search products for Solo.io Products 23 | -------------------------------------------------------------------------------- /.github/data/agent-framework/scenario1/configmap-missing-key.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: agent-framework.solo.io/v1alpha1 2 | kind: Challenge 3 | metadata: 4 | name: configmap-missing-key 5 | spec: 6 | description: Application Expects a Key That Does NOT Exist 7 | prompt: | 8 | There is a kubernetes cluster running in context 'kind-kagent'. 9 | Calling the frontend service at http://frontend-v1:8080 I see an error message telling about issues in the 'backend-v1' service. 10 | To make sure you fixed the environment run the tool `checkKubernetesClusterFixed`. And you have my permission to keep trying out things until you fix the environment and all tests pass. 11 | steps: 12 | - run: | 13 | kubectl --context ${CLUSTER_CTX} apply -f - < /etc/config/app.conf 36 | volumes: 37 | - name: config-volume 38 | configMap: 39 | name: backend-v1-configmap 40 | " 41 | kubectl --context ${CLUSTER_CTX} delete rs -l "app=backend,version=v1" -------------------------------------------------------------------------------- /.github/data/agent-framework/scenario1/deployment-env-mismatch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: agent-framework.solo.io/v1alpha1 2 | kind: Challenge 3 | metadata: 4 | name: deployment-env-mismatch 5 | spec: 6 | description: Update MySQL password but not the apps using it 7 | prompt: | 8 | There is a kubernetes cluster running in context 'kind-kagent'. 9 | Calling the frontend service at http://frontend-v1:8080 I see an error message telling about issues in the 'backend-v3' service. 10 | To make sure you fixed the environment run the tool `checkKubernetesClusterFixed`. And you have my permission to keep trying out things until you fix the environment and all tests pass. 11 | steps: 12 | - run: | 13 | kubectl --context ${CLUSTER_CTX} set env deployment/mysql-v1 MYSQL_ROOT_PASSWORD=mynewpassword 14 | kubectl --context ${CLUSTER_CTX} delete pod -l app=mysql,version=v1 --wait=false -------------------------------------------------------------------------------- /.github/data/agent-framework/scenario1/deployment-impossible-pod-affinity.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: agent-framework.solo.io/v1alpha1 2 | kind: Challenge 3 | metadata: 4 | name: deployment-impossible-pod-affinity 5 | spec: 6 | description: Affinity and an anti-affinity rule that contradict each other, making scheduling impossible. 7 | prompt: | 8 | There is a kubernetes cluster running in context 'kind-kagent'. 9 | Calling the frontend service at http://frontend-v1:8080 I see an error message telling about issues in the 'backend-v1' service. 10 | To make sure you fixed the environment run the tool `checkKubernetesClusterFixed`. And you have my permission to keep trying out things until you fix the environment and all tests pass. 11 | steps: 12 | - run: | 13 | kubectl patch deployment/backend-v1 --context ${CLUSTER_CTX} --type merge -p " 14 | spec: 15 | template: 16 | spec: 17 | affinity: 18 | podAffinity: 19 | requiredDuringSchedulingIgnoredDuringExecution: 20 | - labelSelector: 21 | matchLabels: 22 | app: frontend 23 | topologyKey: "kubernetes.io/hostname" 24 | podAntiAffinity: 25 | requiredDuringSchedulingIgnoredDuringExecution: 26 | - labelSelector: 27 | matchLabels: 28 | app: frontend 29 | topologyKey: "kubernetes.io/hostname" 30 | " -------------------------------------------------------------------------------- /.github/data/agent-framework/scenario1/deployment-low-resources.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: agent-framework.solo.io/v1alpha1 2 | kind: Challenge 3 | metadata: 4 | name: deployment-low-resources 5 | spec: 6 | description: Insufficient resources that will cause an OOM kill 7 | prompt: | 8 | There is a kubernetes cluster running in context 'kind-kagent'. 9 | Calling the frontend service at http://frontend-v1:8080 I see an error message telling about issues in the 'backend-v3' service. 10 | To make sure you fixed the environment run the tool `checkKubernetesClusterFixed`. And you have my permission to keep trying out things until you fix the environment and all tests pass. 11 | steps: 12 | - run: | 13 | kubectl patch deployment mysql-v1 --context ${CLUSTER_CTX} -p '{"spec":{"template":{"spec":{"containers":[{"name":"mysql","resources":{"limits":{"memory":"10Mi"}}}]}}}}' 14 | -------------------------------------------------------------------------------- /.github/data/agent-framework/scenario1/deployment-pod-affinity-wrong-key.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: agent-framework.solo.io/v1alpha1 2 | kind: Challenge 3 | metadata: 4 | name: deployment-pod-affinity-wrong-key 5 | spec: 6 | description: Affinity rule that tries to match a non-existent key (wrong-key), preventing it from scheduling. 7 | prompt: | 8 | There is a kubernetes cluster running in context 'kind-kagent'. 9 | Calling the frontend service at http://frontend-v1:8080 I see an error message telling about issues in the 'backend-v1' service. 10 | To make sure you fixed the environment run the tool `checkKubernetesClusterFixed`. And you have my permission to keep trying out things until you fix the environment and all tests pass. 11 | steps: 12 | - run: | 13 | kubectl patch deployment/backend-v1 --context ${CLUSTER_CTX} --type merge -p " 14 | spec: 15 | template: 16 | spec: 17 | affinity: 18 | podAffinity: 19 | requiredDuringSchedulingIgnoredDuringExecution: 20 | - labelSelector: 21 | matchExpressions: 22 | - key: wrong-key 23 | operator: Exists 24 | topologyKey: kubernetes.io/hostname 25 | " -------------------------------------------------------------------------------- /.github/data/agent-framework/scenario1/deployment-probe-failures.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: agent-framework.solo.io/v1alpha1 2 | kind: Challenge 3 | metadata: 4 | name: deployment-probe-failures 5 | spec: 6 | description: Readiness probe is failing 7 | prompt: | 8 | There is a kubernetes cluster running in context 'kind-kagent'. 9 | Calling the frontend service at http://frontend-v1:8080 is not working. 10 | To make sure you fixed the environment run the tool `checkKubernetesClusterFixed`. 11 | steps: 12 | - run: | 13 | kubectl patch deployment frontend-v1 --context ${CLUSTER_CTX} -p '{"spec":{"template":{"spec":{"containers":[{"name":"frontend","readinessProbe":{"tcpSocket":{"port":9999}}}]}}}}' -------------------------------------------------------------------------------- /.github/data/agent-framework/scenario1/deployment-scaled-down.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: agent-framework.solo.io/v1alpha1 2 | kind: Challenge 3 | metadata: 4 | name: deployment-scaled-down 5 | spec: 6 | description: Scale down mysql-v1 deployment to 0 replicas 7 | prompt: | 8 | There is a kubernetes cluster running in context 'kind-kagent'. 9 | Calling the frontend service at http://frontend-v1:8080 I see an error message telling about issues in the 'backend-v3' service. 10 | To make sure you fixed the environment run the tool `checkKubernetesClusterFixed`. And you have my permission to keep trying out things until you fix the environment and all tests pass. 11 | steps: 12 | - run: | 13 | kubectl --context ${CLUSTER_CTX} scale deployment mysql-v1 --replicas=0 14 | kubectl --context ${CLUSTER_CTX} delete pod -l app=backend,version=v3 --wait=false -------------------------------------------------------------------------------- /.github/data/agent-framework/scenario1/missing-service-selector.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: agent-framework.solo.io/v1alpha1 2 | kind: Challenge 3 | metadata: 4 | name: missing-service-selector 5 | spec: 6 | description: Service selector is missing, service does not route to pods 7 | prompt: | 8 | There is a kubernetes cluster running in context 'kind-kagent'. 9 | Calling the frontend service at http://frontend-v1:8080 I see an error message telling about issues in the 'backend-v2' service. 10 | To make sure you fixed the environment run the tool `checkKubernetesClusterFixed`. And you have my permission to keep trying out things until you fix the environment and all tests pass. 11 | steps: 12 | - run: | 13 | kubectl patch service backend-v2 --context ${CLUSTER_CTX} -p '{"spec":{"selector": null}}' -------------------------------------------------------------------------------- /.github/data/agent-framework/scenario1/network-policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: agent-framework.solo.io/v1alpha1 2 | kind: Challenge 3 | metadata: 4 | name: network-policy 5 | spec: 6 | description: block some internal communication between services 7 | prompt: | 8 | There is a kubernetes cluster running in context 'kind-kagent'. 9 | Calling the frontend service at http://frontend-v1:8080 I see an error message telling about issues communicating with other services. 10 | To make sure you fixed the environment run the tool `checkKubernetesClusterFixed`. And you have my permission to keep trying out things until you fix the environment and all tests pass. 11 | steps: 12 | - run: | 13 | kubectl apply --context ${CLUSTER_CTX} -f - < new Promise(resolve => setTimeout(resolve, ms)), 3 | waitOnFailedTest: (done, currentRetry) => { 4 | if(currentRetry > 0){ 5 | process.stdout.write("."); 6 | setTimeout(done, 1000); 7 | } else { 8 | done(); 9 | } 10 | } 11 | }; 12 | 13 | module.exports = global; -------------------------------------------------------------------------------- /.github/data/agent-framework/scenario1/tests/utils/logging.js: -------------------------------------------------------------------------------- 1 | const debugMode = process.env.RUNNER_DEBUG === '1' || process.env.DEBUG_MODE === 'true'; 2 | 3 | function debugLog(...args) { 4 | if (debugMode && args.length > 0) { 5 | console.log(...args); 6 | } 7 | } 8 | 9 | module.exports = { debugLog }; -------------------------------------------------------------------------------- /.github/workflows/lint-python.yaml: -------------------------------------------------------------------------------- 1 | name: Lint Python Code 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - '**/*.py' # Only trigger on changes to Python files 7 | - 'python/pyproject.toml' 8 | - 'python/uv.lock' 9 | 10 | jobs: 11 | lint: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v4 17 | 18 | - name: Install uv 19 | uses: astral-sh/setup-uv@v5 20 | 21 | - name: Install python 22 | run: uv python install 3.12 23 | 24 | - name: Sync dependencies 25 | working-directory: ./python 26 | run: | 27 | export GIT_LFS_SKIP_SMUDGE=1 28 | uv sync --all-extras 29 | 30 | - name: Run ruff linter 31 | working-directory: ./python 32 | run: uv run ruff check 33 | 34 | - name: Run ruff format check 35 | working-directory: ./python 36 | run: | 37 | if ! uv run ruff format --diff .; then 38 | echo "Ruff formatting issues detected. Please run 'uv run ruff format' locally to fix formatting issues." 39 | exit 1 40 | fi 41 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | e2e-test: 8 | env: 9 | VERSION: v0.0.1-test 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout repository 13 | uses: actions/checkout@v4 14 | 15 | - name: Set up Helm 16 | uses: azure/setup-helm@v4.2.0 17 | with: 18 | version: v3.17.0 19 | 20 | - name: Create k8s Kind Cluster 21 | uses: helm/kind-action@v1 22 | with: 23 | cluster_name: kagent 24 | 25 | - name: Install Kagent 26 | env: 27 | OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} 28 | run: | 29 | make helm-install 30 | kubectl wait --for=condition=Accepted agents.kagent.dev -n kagent --all --timeout=60s 31 | 32 | 33 | - name: Run E2E tests 34 | env: 35 | OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} 36 | run: | 37 | make e2e-test 38 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | python/ @EItanya @peterj 2 | go/ @EItanya @ilackarms 3 | ui/ @peterj 4 | helm/ @EItanya @ilackarms -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Kagent Community Code of Conduct 2 | 3 | All members of the kagent community must abide by the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). 4 | Only by respecting one another can we build a strong and collaborative community. 5 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Security vulnerabilities 2 | 3 | Review how the kagent project handles the lifecycle of Common Vulnerability and Exposures (CVEs). 4 | 5 | ### 📨 Where to report 6 | 7 | To report a security vulnerability, email the private Google group kagent-vulnerability-reports@googlegroups.com. 8 | 9 | ### ✅ When to send a report 10 | 11 | Send a report when: 12 | 13 | You discover that a kagent component has a potential security vulnerability. 14 | You are unsure whether or how a vulnerability affects kagent. 15 | 16 | ### 🔔 Check before sending 17 | 18 | If in doubt, send a private message about potential vulnerabilities such as: 19 | 20 | Any crash, especially in kagent. 21 | Any potential Denial of Service (DoS) attack. 22 | 23 | ### ❌ When NOT to send a report 24 | 25 | Do not send a report for vulnerabilities that are not part of the kagent project, such as: 26 | 27 | You want help configuring kagent components for security purposes. 28 | You want help applying security related updates to your kagent configuration or environment. 29 | Your issue is not related to security vulnerabilities. 30 | Your issue is related to base image dependencies, such as AutoGen. 31 | 32 | ### Evaluation 33 | 34 | The kagent team evaluates vulnerability reports for: 35 | 36 | Severity level, which can affect the priority of the fix 37 | Impact of the vulnerability on kagent code as opposed to backend code 38 | Potential dependencies on third-party or backend code that might delay the remediation process 39 | 40 | The kagent team strives to keep private any vulnerability information with us as part of the remediation process. We only share information on a need-to-know basis to address the issue. 41 | -------------------------------------------------------------------------------- /go/.dockerignore: -------------------------------------------------------------------------------- 1 | ./bin/ -------------------------------------------------------------------------------- /go/Dockerfile: -------------------------------------------------------------------------------- 1 | ### STAGE 1: base image 2 | ARG BASE_IMAGE_REGISTRY=cgr.dev 3 | FROM --platform=linux/$BUILDARCH $BASE_IMAGE_REGISTRY/chainguard/go:latest AS builder 4 | 5 | ARG TARGETPLATFORM 6 | ARG TARGETARCH 7 | 8 | WORKDIR /workspace 9 | # Copy the Go Modules manifests 10 | COPY go.mod go.mod 11 | COPY go.sum go.sum 12 | # cache deps before building and copying source so that we don't need to re-download as much 13 | # and so that source changes don't invalidate our downloaded layer 14 | RUN --mount=type=cache,target=/root/go/pkg/mod,rw \ 15 | --mount=type=cache,target=/root/.cache/go-build,rw \ 16 | go mod download 17 | 18 | # Copy the go source 19 | COPY autogen autogen 20 | COPY controller controller 21 | 22 | # Build 23 | # the GOARCH has not a default value to allow the binary be built according to the host where the command 24 | # was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO 25 | # the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, 26 | # by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. 27 | RUN --mount=type=cache,target=/root/go/pkg/mod,rw \ 28 | --mount=type=cache,target=/root/.cache/go-build,rw \ 29 | CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager controller/cmd/main.go 30 | 31 | # Use distroless as minimal base image to package the manager binary 32 | # Refer to https://github.com/GoogleContainerTools/distroless for more details 33 | FROM gcr.io/distroless/static:nonroot 34 | WORKDIR / 35 | COPY --from=builder /workspace/manager . 36 | USER 65532:65532 37 | 38 | LABEL org.opencontainers.image.source=https://github.com/kagent-dev/kagent 39 | LABEL org.opencontainers.image.description="Kagent controller is the controller for the Kagent app." 40 | 41 | ENTRYPOINT ["/manager"] 42 | -------------------------------------------------------------------------------- /go/autogen/api/context.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | type ChatCompletionContextConfig struct{} 4 | 5 | func (c *ChatCompletionContextConfig) ToConfig() (map[string]interface{}, error) { 6 | return toConfig(c) 7 | } 8 | 9 | func (c *ChatCompletionContextConfig) FromConfig(config map[string]interface{}) error { 10 | return fromConfig(c, config) 11 | } 12 | -------------------------------------------------------------------------------- /go/autogen/api/memory.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | type PineconeMemoryConfig struct { 4 | APIKey string `json:"api_key"` 5 | IndexHost string `json:"index_host"` 6 | TopK int `json:"top_k"` 7 | Namespace string `json:"namespace"` 8 | RecordFields []string `json:"record_fields"` 9 | ScoreThreshold float64 `json:"score_threshold"` 10 | } 11 | 12 | func (c *PineconeMemoryConfig) ToConfig() (map[string]interface{}, error) { 13 | return toConfig(c) 14 | } 15 | 16 | func (c *PineconeMemoryConfig) FromConfig(config map[string]interface{}) error { 17 | return fromConfig(c, config) 18 | } 19 | -------------------------------------------------------------------------------- /go/autogen/api/tool_servers.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | type ToolServerConfig struct { 4 | //ONEOF 5 | *StdioMcpServerConfig 6 | *SseMcpServerConfig 7 | } 8 | 9 | func (c *ToolServerConfig) ToConfig() (map[string]interface{}, error) { 10 | return toConfig(c) 11 | } 12 | 13 | func (c *ToolServerConfig) FromConfig(config map[string]interface{}) error { 14 | return fromConfig(c, config) 15 | } 16 | 17 | type StdioMcpServerConfig struct { 18 | Command string `json:"command"` 19 | Args []string `json:"args,omitempty"` 20 | Env map[string]string `json:"env,omitempty"` 21 | } 22 | 23 | type SseMcpServerConfig struct { 24 | URL string `json:"url"` 25 | Headers map[string]interface{} `json:"headers,omitempty"` 26 | Timeout int `json:"timeout,omitempty"` 27 | SseReadTimeout int `json:"sse_read_timeout,omitempty"` 28 | } 29 | 30 | type MCPToolConfig struct { 31 | // can be StdioMcpServerConfig | SseMcpServerConfig 32 | ServerParams any `json:"server_params"` 33 | Tool MCPTool `json:"tool"` 34 | } 35 | 36 | type MCPTool struct { 37 | Name string `json:"name"` 38 | Description string `json:"description"` 39 | InputSchema any `json:"input_schema"` 40 | } 41 | -------------------------------------------------------------------------------- /go/autogen/api/tools.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | // HTTPToolConfig represents the configuration for HTTP tools 4 | type HTTPToolConfig struct { 5 | Name string `json:"name"` 6 | Description string `json:"description"` 7 | Scheme string `json:"scheme"` 8 | Host string `json:"host"` 9 | Port int `json:"port"` 10 | Path string `json:"path"` 11 | Method string `json:"method"` 12 | Headers map[string]string `json:"headers"` 13 | JSONSchema map[string]interface{} `json:"json_schema"` 14 | } 15 | 16 | func (c *HTTPToolConfig) ToConfig() (map[string]interface{}, error) { 17 | return toConfig(c) 18 | } 19 | 20 | func (c *HTTPToolConfig) FromConfig(config map[string]interface{}) error { 21 | return fromConfig(c, config) 22 | } 23 | 24 | type GenericToolConfig map[string]interface{} 25 | 26 | func (c *GenericToolConfig) ToConfig() (map[string]interface{}, error) { 27 | return toConfig(c) 28 | } 29 | 30 | func (c *GenericToolConfig) FromConfig(config map[string]interface{}) error { 31 | return fromConfig(c, config) 32 | } 33 | 34 | type TeamToolConfig struct { 35 | Name string `json:"name"` 36 | Description string `json:"description"` 37 | Team *Component `json:"team"` 38 | } 39 | 40 | func (c *TeamToolConfig) ToConfig() (map[string]interface{}, error) { 41 | return toConfig(c) 42 | } 43 | 44 | func (c *TeamToolConfig) FromConfig(config map[string]interface{}) error { 45 | return fromConfig(c, config) 46 | } 47 | -------------------------------------------------------------------------------- /go/autogen/api/types.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | type Component struct { 8 | Provider string `json:"provider"` 9 | ComponentType string `json:"component_type"` 10 | Version int `json:"version"` 11 | ComponentVersion int `json:"component_version"` 12 | Description string `json:"description"` 13 | Label string `json:"label"` 14 | Config map[string]interface{} `json:"config"` 15 | } 16 | 17 | func (c *Component) ToConfig() (map[string]interface{}, error) { 18 | if c == nil { 19 | return nil, nil 20 | } 21 | 22 | return toConfig(c) 23 | } 24 | 25 | func MustToConfig(c ComponentConfig) map[string]interface{} { 26 | config, err := c.ToConfig() 27 | if err != nil { 28 | panic(err) 29 | } 30 | return config 31 | } 32 | 33 | func MustFromConfig(c ComponentConfig, config map[string]interface{}) { 34 | err := c.FromConfig(config) 35 | if err != nil { 36 | panic(err) 37 | } 38 | } 39 | 40 | type ComponentConfig interface { 41 | ToConfig() (map[string]interface{}, error) 42 | FromConfig(map[string]interface{}) error 43 | } 44 | 45 | func toConfig(c any) (map[string]interface{}, error) { 46 | byt, err := json.Marshal(c) 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | result := make(map[string]interface{}) 52 | err = json.Unmarshal(byt, &result) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | return result, nil 58 | } 59 | 60 | func fromConfig(c any, config map[string]interface{}) error { 61 | byt, err := json.Marshal(config) 62 | if err != nil { 63 | return err 64 | } 65 | 66 | return json.Unmarshal(byt, c) 67 | } 68 | -------------------------------------------------------------------------------- /go/autogen/client/feedback.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import "fmt" 4 | 5 | func (c *client) CreateFeedback(feedback *FeedbackSubmission) error { 6 | err := c.doRequest("POST", "/feedback/", feedback, nil) 7 | if err != nil { 8 | return err 9 | } 10 | 11 | return nil 12 | } 13 | 14 | func (c *client) ListFeedback(userID string) ([]*FeedbackSubmission, error) { 15 | var response []*FeedbackSubmission 16 | err := c.doRequest("GET", fmt.Sprintf("/feedback/?user_id=%s", userID), nil, &response) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | return response, nil 22 | } 23 | -------------------------------------------------------------------------------- /go/autogen/client/invoke.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/kagent-dev/kagent/go/autogen/api" 5 | ) 6 | 7 | type InvokeTaskRequest struct { 8 | Task string `json:"task"` 9 | TeamConfig *api.Component `json:"team_config"` 10 | } 11 | 12 | type InvokeTaskResult struct { 13 | Duration float64 `json:"duration"` 14 | TaskResult TaskResult `json:"task_result"` 15 | Usage string `json:"usage"` 16 | } 17 | 18 | func (c *client) InvokeTask(req *InvokeTaskRequest) (*InvokeTaskResult, error) { 19 | var invoke InvokeTaskResult 20 | err := c.doRequest("POST", "/invoke", req, &invoke) 21 | return &invoke, err 22 | } 23 | 24 | func (c *client) InvokeTaskStream(req *InvokeTaskRequest) (<-chan *SseEvent, error) { 25 | resp, err := c.startRequest("POST", "/invoke/stream", req) 26 | if err != nil { 27 | return nil, err 28 | } 29 | ch := streamSseResponse(resp.Body) 30 | return ch, nil 31 | } 32 | -------------------------------------------------------------------------------- /go/autogen/client/models.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | func (c *client) ListSupportedModels() (*ProviderModels, error) { 4 | var models ProviderModels 5 | err := c.doRequest("GET", "/models", nil, &models) 6 | return &models, err 7 | } 8 | -------------------------------------------------------------------------------- /go/autogen/client/run.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/google/uuid" 7 | ) 8 | 9 | func (c *client) CreateRun(req *CreateRunRequest) (*CreateRunResult, error) { 10 | var run CreateRunResult 11 | err := c.doRequest("POST", "/runs", req, &run) 12 | return &run, err 13 | } 14 | 15 | func (c *client) GetRun(runID int) (*Run, error) { 16 | 17 | var run Run 18 | err := c.doRequest("GET", fmt.Sprintf("/runs/%d", runID), nil, &run) 19 | return &run, err 20 | } 21 | 22 | func (c *client) ListRuns(userID string) ([]*Run, error) { 23 | // Go through all sessions and then retrieve all runs for each session 24 | var sessions []Session 25 | err := c.doRequest("GET", fmt.Sprintf("/sessions/?user_id=%s", userID), nil, &sessions) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | // For each session, get the run information 31 | var runs []*Run 32 | for _, session := range sessions { 33 | sessionRuns, err := c.ListSessionRuns(session.ID, userID) 34 | if err != nil { 35 | return nil, err 36 | } 37 | runs = append(runs, sessionRuns...) 38 | } 39 | return runs, nil 40 | } 41 | 42 | func (c *client) GetRunMessages(runID uuid.UUID) ([]*RunMessage, error) { 43 | var messages []*RunMessage 44 | err := c.doRequest("GET", fmt.Sprintf("/runs/%s/messages", runID), nil, &messages) 45 | return messages, err 46 | } 47 | 48 | func (c *client) DeleteRun(runID uuid.UUID) error { 49 | return c.doRequest("DELETE", fmt.Sprintf("/runs/%s", runID), nil, nil) 50 | } 51 | -------------------------------------------------------------------------------- /go/autogen/client/team.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func (c *client) ListTeams(userID string) ([]*Team, error) { 8 | var teams []*Team 9 | err := c.doRequest("GET", fmt.Sprintf("/teams/?user_id=%s", userID), nil, &teams) 10 | return teams, err 11 | } 12 | 13 | func (c *client) CreateTeam(team *Team) error { 14 | return c.doRequest("POST", "/teams/", team, team) 15 | } 16 | 17 | func (c *client) GetTeamByID(teamID int, userID string) (*Team, error) { 18 | var team *Team 19 | err := c.doRequest("GET", fmt.Sprintf("/teams/%d?user_id=%s", teamID, userID), nil, &team) 20 | return team, err 21 | } 22 | 23 | func (c *client) GetTeam(teamLabel string, userID string) (*Team, error) { 24 | allTeams, err := c.ListTeams(userID) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | for _, team := range allTeams { 30 | if team.Component.Label == teamLabel { 31 | return team, nil 32 | } 33 | } 34 | 35 | return nil, nil 36 | } 37 | 38 | func (c *client) DeleteTeam(teamID int, userID string) error { 39 | return c.doRequest("DELETE", fmt.Sprintf("/teams/%d?user_id=%s", teamID, userID), nil, nil) 40 | } 41 | -------------------------------------------------------------------------------- /go/autogen/client/tools.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func (c *client) ListTools(userID string) ([]*Tool, error) { 8 | var tools []*Tool 9 | err := c.doRequest("GET", fmt.Sprintf("/tools/?user_id=%s", userID), nil, &tools) 10 | return tools, err 11 | } 12 | 13 | func (c *client) GetTool(provider string, userID string) (*Tool, error) { 14 | allTools, err := c.ListTools(userID) 15 | if err != nil { 16 | return nil, err 17 | } 18 | 19 | for _, tool := range allTools { 20 | if tool.Component.Provider == provider { 21 | return tool, nil 22 | } 23 | } 24 | 25 | return nil, nil 26 | } 27 | -------------------------------------------------------------------------------- /go/autogen/client/validate.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/kagent-dev/kagent/go/autogen/api" 7 | ) 8 | 9 | type ValidationRequest struct { 10 | Component *api.Component `json:"component"` 11 | } 12 | 13 | type ValidationError struct { 14 | Field string `json:"field"` 15 | Error string `json:"error"` 16 | Suggestion *string `json:"suggestion,omitempty"` 17 | } 18 | 19 | type ValidationResponse struct { 20 | IsValid bool `json:"is_valid"` 21 | Errors []*ValidationError `json:"errors"` 22 | Warnings []*ValidationError `json:"warnings"` 23 | } 24 | 25 | func (r ValidationResponse) ErrorMsg() string { 26 | var msg string 27 | for _, e := range r.Errors { 28 | msg += fmt.Sprintf("Error: %s\n [%s]\n", e.Error, e.Field) 29 | if e.Suggestion != nil { 30 | msg += fmt.Sprintf("Suggestion: %s\n", *e.Suggestion) 31 | } 32 | } 33 | for _, w := range r.Warnings { 34 | msg += fmt.Sprintf("Warning: %s\n [%s]\n", w.Error, w.Field) 35 | if w.Suggestion != nil { 36 | msg += fmt.Sprintf("Suggestion: %s\n", *w.Suggestion) 37 | } 38 | } 39 | 40 | return msg 41 | } 42 | 43 | func (c *client) Validate(req *ValidationRequest) (*ValidationResponse, error) { 44 | var resp ValidationResponse 45 | err := c.doRequest("POST", "/validate", req, &resp) 46 | return &resp, err 47 | } 48 | -------------------------------------------------------------------------------- /go/cli/Makefile: -------------------------------------------------------------------------------- 1 | VERSION ?= dev 2 | GIT_COMMIT := $(shell git rev-parse --short HEAD || echo "unknown") 3 | BUILD_DATE := $(shell date -u '+%Y-%m-%d') 4 | 5 | LDFLAGS := -X github.com/kagent-dev/kagent/go/cli/internal/cli.Version=$(VERSION) \ 6 | -X github.com/kagent-dev/kagent/go/cli/internal/cli.GitCommit=$(GIT_COMMIT) \ 7 | -X github.com/kagent-dev/kagent/go/cli/internal/cli.BuildDate=$(BUILD_DATE) 8 | 9 | .PHONY: build 10 | build: 11 | go build -ldflags "$(LDFLAGS)" -o bin/kagent ./cmd/kagent 12 | 13 | .PHONY: install 14 | install: 15 | go install -ldflags "$(LDFLAGS)" ./cmd/kagent 16 | 17 | .PHONY: clean 18 | clean: 19 | rm -rf bin/ 20 | 21 | .PHONY: test 22 | test: 23 | go test ./... 24 | 25 | .PHONY: deps 26 | deps: 27 | go mod download 28 | go mod tidy 29 | 30 | .DEFAULT_GOAL := build -------------------------------------------------------------------------------- /go/cli/internal/cli/dashboard.go: -------------------------------------------------------------------------------- 1 | //go:build !darwin 2 | 3 | package cli 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "os" 9 | 10 | "github.com/kagent-dev/kagent/go/cli/internal/config" 11 | ) 12 | 13 | func DashboardCmd(ctx context.Context, cfg *config.Config) { 14 | fmt.Fprintln(os.Stderr, "Dashboard is not available on this platform") 15 | fmt.Fprintln(os.Stderr, "You can easily start the dashboard by running:") 16 | fmt.Fprintln(os.Stderr, "kubectl port-forward -n kagent service/kagent 8082:80") 17 | fmt.Fprintln(os.Stderr, "and then opening http://localhost:8082 in your browser") 18 | } 19 | -------------------------------------------------------------------------------- /go/cli/internal/cli/dashboard_darwin.go: -------------------------------------------------------------------------------- 1 | //go:build darwin 2 | 3 | package cli 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "os" 9 | "os/exec" 10 | "strings" 11 | "time" 12 | 13 | "github.com/kagent-dev/kagent/go/cli/internal/config" 14 | ) 15 | 16 | func DashboardCmd(ctx context.Context, cfg *config.Config) { 17 | ctx, cancel := context.WithCancel(ctx) 18 | cmd := exec.CommandContext(ctx, "kubectl", "-n", cfg.Namespace, "port-forward", "service/kagent", "8082:80") 19 | 20 | defer func() { 21 | cancel() 22 | if err := cmd.Wait(); err != nil { // These 2 errors are expected 23 | if !strings.Contains(err.Error(), "signal: killed") && !strings.Contains(err.Error(), "exec: not started") { 24 | fmt.Fprintf(os.Stderr, "Error waiting for port-forward to exit: %v\n", err) 25 | } 26 | } 27 | }() 28 | 29 | if err := cmd.Start(); err != nil { 30 | fmt.Fprintf(os.Stderr, "Error port-forwarding kagent: %v\n", err) 31 | return 32 | } 33 | 34 | // Wait for the port-forward to start 35 | time.Sleep(1 * time.Second) 36 | 37 | // Open the dashboard in the browser 38 | openCmd := exec.CommandContext(ctx, "open", "http://localhost:8082") 39 | if err := openCmd.Run(); err != nil { 40 | fmt.Fprintf(os.Stderr, "Error opening kagent dashboard: %v\n", err) 41 | } 42 | 43 | fmt.Fprintln(os.Stdout, "kagent dashboard is available at http://localhost:8082") 44 | 45 | fmt.Println("Press the Enter Key to stop the port-forward...") 46 | fmt.Scanln() // wait for Enter Key 47 | } 48 | -------------------------------------------------------------------------------- /go/cli/internal/cli/delete.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/abiosoft/ishell/v2" 7 | autogen_client "github.com/kagent-dev/kagent/go/autogen/client" 8 | "github.com/kagent-dev/kagent/go/cli/internal/config" 9 | ) 10 | 11 | func DeleteCmd(c *ishell.Context) { 12 | if len(c.Args) < 2 { 13 | c.Println("Usage: delete [resource_type] id") 14 | return 15 | } 16 | 17 | c.Println(c.Args) 18 | 19 | resourceType := c.Args[0] 20 | id := c.Args[1] 21 | 22 | cfg, err := config.Get() 23 | if err != nil { 24 | c.Printf("Failed to get config: %v\n", err) 25 | return 26 | } 27 | 28 | client := autogen_client.New(cfg.APIURL) 29 | 30 | switch resourceType { 31 | case "team": 32 | teamID, err := strconv.Atoi(id) 33 | if err != nil { 34 | c.Printf("Invalid team ID: %v\n", err) 35 | return 36 | } 37 | if err := client.DeleteTeam(teamID, cfg.UserID); err != nil { 38 | c.Printf("Error deleting team: %v\n", err) 39 | return 40 | } 41 | default: 42 | c.Println("Invalid resource type. Valid resource types are: team") 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /go/cli/internal/cli/format.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "iter" 7 | "slices" 8 | 9 | "github.com/jedib0t/go-pretty/v6/table" 10 | "github.com/spf13/viper" 11 | ) 12 | 13 | type OutputFormat string 14 | 15 | const ( 16 | OutputFormatJSON OutputFormat = "json" 17 | OutputFormatTable OutputFormat = "table" 18 | ) 19 | 20 | // Map returns an iterator over the slice, applying the function f to each element. 21 | func Map[E any, F any](s iter.Seq[E], f func(E) F) iter.Seq[F] { 22 | return func(yield func(F) bool) { 23 | for v := range s { 24 | if !yield(f(v)) { 25 | return 26 | } 27 | } 28 | } 29 | } 30 | 31 | func Filter[E any](s iter.Seq[E], f func(E) bool) iter.Seq[E] { 32 | return func(yield func(E) bool) { 33 | for v := range s { 34 | if f(v) && !yield(v) { 35 | return 36 | } 37 | } 38 | } 39 | } 40 | 41 | func printOutput(data interface{}, tableHeaders []string, tableRows [][]string) error { 42 | format := OutputFormat(viper.GetString("output_format")) 43 | 44 | tw := table.NewWriter() 45 | headers := slices.Collect(Map(slices.Values(tableHeaders), func(header string) interface{} { 46 | return header 47 | })) 48 | tw.AppendHeader(headers) 49 | rows := slices.Collect(Map(slices.Values(tableRows), func(row []string) table.Row { 50 | return slices.Collect(Map(slices.Values(row), func(cell string) interface{} { 51 | return cell 52 | })) 53 | })) 54 | tw.AppendRows(rows) 55 | 56 | switch format { 57 | case OutputFormatJSON: 58 | return printJSON(data) 59 | case OutputFormatTable: 60 | fmt.Println(tw.Render()) 61 | return nil 62 | default: 63 | return fmt.Errorf("unknown output format: %s", format) 64 | } 65 | } 66 | 67 | func printJSON(data interface{}) error { 68 | output, err := json.MarshalIndent(data, "", " ") 69 | if err != nil { 70 | return fmt.Errorf("error formatting JSON: %w", err) 71 | } 72 | fmt.Println(string(output)) 73 | return nil 74 | } 75 | -------------------------------------------------------------------------------- /go/cli/internal/cli/version.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | autogen_client "github.com/kagent-dev/kagent/go/autogen/client" 8 | "github.com/kagent-dev/kagent/go/cli/internal/config" 9 | ) 10 | 11 | var ( 12 | // These variables should be set during build time using -ldflags 13 | Version = "dev" 14 | GitCommit = "none" 15 | BuildDate = "unknown" 16 | ) 17 | 18 | func VersionCmd(cfg *config.Config) { 19 | fmt.Fprintf(os.Stdout, "kagent version %s\n", Version) 20 | fmt.Fprintf(os.Stdout, "git commit: %s\n", GitCommit) 21 | fmt.Fprintf(os.Stdout, "build date: %s\n", BuildDate) 22 | 23 | client := autogen_client.New(cfg.APIURL) 24 | version, err := client.GetVersion() 25 | if err != nil { 26 | fmt.Fprintln(os.Stderr, "Warning: Could not fetch backend version") 27 | } else { 28 | fmt.Fprintf(os.Stdout, "backend version: %s\n", version) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /go/cli/internal/config/utils_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "os" 5 | "path" 6 | "testing" 7 | ) 8 | 9 | func TestGetConfigDirFirstRun(t *testing.T) { 10 | homeDir := t.TempDir() 11 | checkGetConfig(t, homeDir) 12 | } 13 | 14 | func TestGetConfigDirSubsequentRun(t *testing.T) { 15 | homeDir := t.TempDir() 16 | checkGetConfig(t, homeDir) 17 | checkGetConfig(t, homeDir) 18 | } 19 | 20 | func TestHandlesErrorWhenCreatingConfigDir(t *testing.T) { 21 | homeDir := t.TempDir() 22 | nonExistentDir := path.Join(homeDir, "/invalid/path") 23 | result, err := GetConfigDir(nonExistentDir) 24 | if err == nil { 25 | t.Fatalf("Expected error, but got nil") 26 | } 27 | if result != "" { 28 | t.Fatalf("Expected empty string, but got %s", result) 29 | } 30 | } 31 | 32 | func checkGetConfig(t *testing.T, homeDir string) { 33 | configDir, err := GetConfigDir(homeDir) 34 | 35 | //check for error 36 | if err != nil { 37 | t.Fatalf("Expected no error, but got %v", err) 38 | } 39 | 40 | //check it's equal to the expected path 41 | expectedDir := path.Join(homeDir, ".config", "kagent") 42 | if configDir != expectedDir { 43 | t.Fatalf("Expected %s, but got %s", expectedDir, configDir) 44 | } 45 | 46 | //check kagent folder is exists 47 | if _, err := os.Stat(expectedDir); os.IsNotExist(err) { 48 | t.Fatalf("Expected %s to exist, but it does not", path.Join(homeDir, "kagent")) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /go/config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: manager-role 6 | rules: 7 | - apiGroups: 8 | - agent.kagent.dev 9 | resources: 10 | - toolservers 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - agent.kagent.dev 21 | resources: 22 | - toolservers/finalizers 23 | verbs: 24 | - update 25 | - apiGroups: 26 | - agent.kagent.dev 27 | resources: 28 | - toolservers/status 29 | verbs: 30 | - get 31 | - patch 32 | - update 33 | - apiGroups: 34 | - kagent.dev 35 | resources: 36 | - agents 37 | - memories 38 | - modelconfigs 39 | - teams 40 | verbs: 41 | - create 42 | - delete 43 | - get 44 | - list 45 | - patch 46 | - update 47 | - watch 48 | - apiGroups: 49 | - kagent.dev 50 | resources: 51 | - agents/finalizers 52 | - memories/finalizers 53 | - modelconfigs/finalizers 54 | - teams/finalizers 55 | verbs: 56 | - update 57 | - apiGroups: 58 | - kagent.dev 59 | resources: 60 | - agents/status 61 | - memories/status 62 | - modelconfigs/status 63 | - teams/status 64 | verbs: 65 | - get 66 | - patch 67 | - update 68 | -------------------------------------------------------------------------------- /go/controller/.dockerignore: -------------------------------------------------------------------------------- 1 | # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file 2 | # Ignore build and test binaries. 3 | bin/ 4 | -------------------------------------------------------------------------------- /go/controller/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | bin/* 8 | Dockerfile.cross 9 | 10 | # Test binary, built with `go test -c` 11 | *.test 12 | 13 | # Output of the go coverage tool, specifically when used with LiteIDE 14 | *.out 15 | 16 | # Go workspace file 17 | go.work 18 | 19 | # Kubernetes Generated files - skip generated files, except for vendored files 20 | !vendor/**/zz_generated.* 21 | 22 | # editor and IDE paraphernalia 23 | .idea 24 | .vscode 25 | *.swp 26 | *.swo 27 | *~ 28 | -------------------------------------------------------------------------------- /go/controller/PROJECT: -------------------------------------------------------------------------------- 1 | # Code generated by tool. DO NOT EDIT. 2 | # This file is used to track the info used to scaffold your project 3 | # and allow the plugins properly work. 4 | # More info: https://book.kubebuilder.io/reference/project-config.html 5 | domain: kagent.dev 6 | layout: 7 | - go.kubebuilder.io/v4 8 | projectName: controller 9 | repo: github.com/kagent-dev/kagent/go/controller 10 | resources: 11 | - api: 12 | crdVersion: v1 13 | namespaced: true 14 | controller: true 15 | domain: kagent.dev 16 | group: agent 17 | kind: AutogenTeam 18 | path: github.com/kagent-dev/kagent/go/controller/api/v1alpha1 19 | version: v1alpha1 20 | - api: 21 | crdVersion: v1 22 | namespaced: true 23 | controller: true 24 | domain: kagent.dev 25 | group: agent 26 | kind: AutogenAgent 27 | path: github.com/kagent-dev/kagent/go/controller/api/v1alpha1 28 | version: v1alpha1 29 | - api: 30 | crdVersion: v1 31 | namespaced: true 32 | controller: true 33 | domain: kagent.dev 34 | group: agent 35 | kind: AutogenModelConfig 36 | path: github.com/kagent-dev/kagent/go/controller/api/v1alpha1 37 | version: v1alpha1 38 | - api: 39 | crdVersion: v1 40 | namespaced: true 41 | controller: true 42 | domain: kagent.dev 43 | group: agent 44 | kind: ToolServer 45 | path: github.com/kagent-dev/kagent/go/controller/api/v1alpha1 46 | version: v1alpha1 47 | version: "3" 48 | -------------------------------------------------------------------------------- /go/controller/api/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1alpha1 contains API Schema definitions for the agent v1alpha1 API group. 18 | // +kubebuilder:object:generate=true 19 | // +groupName=kagent.dev 20 | package v1alpha1 21 | 22 | import ( 23 | "k8s.io/apimachinery/pkg/runtime/schema" 24 | "sigs.k8s.io/controller-runtime/pkg/scheme" 25 | ) 26 | 27 | var ( 28 | // GroupVersion is group version used to register these objects. 29 | GroupVersion = schema.GroupVersion{Group: "kagent.dev", Version: "v1alpha1"} 30 | 31 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme. 32 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 33 | 34 | // AddToScheme adds the types in this group-version to the given scheme. 35 | AddToScheme = SchemeBuilder.AddToScheme 36 | ) 37 | -------------------------------------------------------------------------------- /go/controller/hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ -------------------------------------------------------------------------------- /go/controller/internal/a2a/a2a_reconciler.go: -------------------------------------------------------------------------------- 1 | package a2a 2 | 3 | import ( 4 | "context" 5 | 6 | autogen_client "github.com/kagent-dev/kagent/go/autogen/client" 7 | "github.com/kagent-dev/kagent/go/controller/api/v1alpha1" 8 | ctrl "sigs.k8s.io/controller-runtime" 9 | ) 10 | 11 | var ( 12 | reconcileLog = ctrl.Log.WithName("a2a_reconcile") 13 | ) 14 | 15 | type A2AReconciler interface { 16 | ReconcileAutogenAgent( 17 | ctx context.Context, 18 | agent *v1alpha1.Agent, 19 | autogenTeam *autogen_client.Team, 20 | ) error 21 | 22 | ReconcileAutogenAgentDeletion( 23 | agentNamespace string, 24 | agentName string, 25 | ) 26 | } 27 | 28 | type a2aReconciler struct { 29 | a2aTranslator AutogenA2ATranslator 30 | autogenClient autogen_client.Client 31 | a2aHandler A2AHandlerMux 32 | } 33 | 34 | func NewAutogenReconciler( 35 | autogenClient autogen_client.Client, 36 | a2aHandler A2AHandlerMux, 37 | a2aBaseUrl string, 38 | ) A2AReconciler { 39 | return &a2aReconciler{ 40 | a2aTranslator: NewAutogenA2ATranslator(a2aBaseUrl, autogenClient), 41 | autogenClient: autogenClient, 42 | a2aHandler: a2aHandler, 43 | } 44 | } 45 | 46 | func (a *a2aReconciler) ReconcileAutogenAgent( 47 | ctx context.Context, 48 | agent *v1alpha1.Agent, 49 | autogenTeam *autogen_client.Team, 50 | ) error { 51 | params, err := a.a2aTranslator.TranslateHandlerForAgent(ctx, agent, autogenTeam) 52 | if err != nil { 53 | return err 54 | } 55 | if params == nil { 56 | reconcileLog.Info("No a2a handler found for agent, a2a will be disabled", "agent", agent.Name) 57 | return nil 58 | } 59 | 60 | return a.a2aHandler.SetAgentHandler( 61 | agent.Namespace, agent.Name, 62 | params, 63 | ) 64 | } 65 | 66 | func (a *a2aReconciler) ReconcileAutogenAgentDeletion( 67 | agentNamespace string, 68 | agentName string, 69 | ) { 70 | a.a2aHandler.RemoveAgentHandler( 71 | agentNamespace, agentName, 72 | ) 73 | } 74 | -------------------------------------------------------------------------------- /go/controller/internal/autogen/testdata/inputs/agent_with_builtin_tools.yaml: -------------------------------------------------------------------------------- 1 | operation: translateAgent 2 | targetObject: agent-with-tools 3 | namespace: test 4 | objects: 5 | - apiVersion: v1 6 | kind: Secret 7 | metadata: 8 | name: openai-secret 9 | namespace: test 10 | data: 11 | api-key: c2stdGVzdC1hcGkta2V5 # base64 encoded "sk-test-api-key" 12 | - apiVersion: kagent.dev/v1alpha1 13 | kind: ModelConfig 14 | metadata: 15 | name: tool-model 16 | namespace: test 17 | spec: 18 | provider: OpenAI 19 | model: gpt-4o 20 | apiKeySecretRef: openai-secret 21 | apiKeySecretKey: api-key 22 | openAI: 23 | temperature: "0.2" 24 | maxTokens: 2048 25 | - apiVersion: kagent.dev/v1alpha1 26 | kind: Agent 27 | metadata: 28 | name: agent-with-tools 29 | namespace: test 30 | spec: 31 | description: An agent with builtin tools 32 | systemMessage: You are a helpful assistant with access to tools. 33 | modelConfig: tool-model 34 | tools: 35 | - builtin: 36 | name: kagent.tools.prometheus.GeneratePromQLTool 37 | config: 38 | timeout: 30 39 | - builtin: 40 | name: kagent.tools.docs.QueryTool 41 | config: 42 | max_results: 5 -------------------------------------------------------------------------------- /go/controller/internal/autogen/testdata/inputs/agent_with_memory.yaml: -------------------------------------------------------------------------------- 1 | operation: translateAgent 2 | targetObject: agent-with-memory 3 | namespace: test 4 | objects: 5 | - apiVersion: v1 6 | kind: Secret 7 | metadata: 8 | name: openai-secret 9 | namespace: test 10 | data: 11 | api-key: c2stdGVzdC1hcGkta2V5 # base64 encoded "sk-test-api-key" 12 | - apiVersion: v1 13 | kind: Secret 14 | metadata: 15 | name: pinecone-secret 16 | namespace: test 17 | data: 18 | api-key: cGluZWNvbmUtYXBpLWtleQ== # base64 encoded "pinecone-api-key" 19 | - apiVersion: kagent.dev/v1alpha1 20 | kind: ModelConfig 21 | metadata: 22 | name: memory-model 23 | namespace: test 24 | spec: 25 | provider: OpenAI 26 | model: gpt-4o 27 | apiKeySecretRef: openai-secret 28 | apiKeySecretKey: api-key 29 | - apiVersion: kagent.dev/v1alpha1 30 | kind: Memory 31 | metadata: 32 | name: vector-memory 33 | namespace: test 34 | spec: 35 | provider: Pinecone 36 | apiKeySecretRef: pinecone-secret 37 | apiKeySecretKey: api-key 38 | pinecone: 39 | indexHost: "https://test-index.pinecone.io" 40 | topK: 5 41 | namespace: "test-namespace" 42 | recordFields: ["content", "metadata"] 43 | scoreThreshold: "0.7" 44 | - apiVersion: kagent.dev/v1alpha1 45 | kind: Agent 46 | metadata: 47 | name: agent-with-memory 48 | namespace: test 49 | spec: 50 | description: An agent with vector memory 51 | systemMessage: You are an assistant with access to long-term memory. 52 | modelConfig: memory-model 53 | memory: 54 | - vector-memory 55 | tools: [] -------------------------------------------------------------------------------- /go/controller/internal/autogen/testdata/inputs/agent_with_nested_agent.yaml: -------------------------------------------------------------------------------- 1 | operation: translateAgent 2 | targetObject: parent-agent 3 | namespace: test 4 | objects: 5 | - apiVersion: v1 6 | kind: Secret 7 | metadata: 8 | name: openai-secret 9 | namespace: test 10 | data: 11 | api-key: c2stdGVzdC1hcGkta2V5 # base64 encoded "sk-test-api-key" 12 | - apiVersion: kagent.dev/v1alpha1 13 | kind: ModelConfig 14 | metadata: 15 | name: nested-model 16 | namespace: test 17 | spec: 18 | provider: OpenAI 19 | model: gpt-4o 20 | apiKeySecretRef: openai-secret 21 | apiKeySecretKey: api-key 22 | - apiVersion: kagent.dev/v1alpha1 23 | kind: Agent 24 | metadata: 25 | name: specialist-agent 26 | namespace: test 27 | spec: 28 | description: A specialist agent for math problems 29 | systemMessage: You are a math specialist. Focus on solving mathematical problems step by step. 30 | modelConfig: nested-model 31 | tools: [] 32 | - apiVersion: kagent.dev/v1alpha1 33 | kind: Agent 34 | metadata: 35 | name: parent-agent 36 | namespace: test 37 | spec: 38 | description: A parent agent that can delegate to specialists 39 | systemMessage: You are a coordinating agent that can delegate tasks to specialists. 40 | modelConfig: nested-model 41 | tools: 42 | - agent: 43 | ref: specialist-agent -------------------------------------------------------------------------------- /go/controller/internal/autogen/testdata/inputs/anthropic_agent.yaml: -------------------------------------------------------------------------------- 1 | operation: translateAgent 2 | targetObject: anthropic-agent 3 | namespace: test 4 | objects: 5 | - apiVersion: v1 6 | kind: Secret 7 | metadata: 8 | name: anthropic-secret 9 | namespace: test 10 | data: 11 | api-key: YW50aHJvcGljLWFwaS1rZXk= # base64 encoded "anthropic-api-key" 12 | - apiVersion: kagent.dev/v1alpha1 13 | kind: ModelConfig 14 | metadata: 15 | name: anthropic-model 16 | namespace: test 17 | spec: 18 | provider: Anthropic 19 | model: claude-3-sonnet-20240229 20 | apiKeySecretRef: anthropic-secret 21 | apiKeySecretKey: api-key 22 | anthropic: 23 | baseURL: "https://api.anthropic.com" 24 | temperature: "0.3" 25 | maxTokens: 4096 26 | topP: "0.9" 27 | topK: 40 28 | - apiVersion: kagent.dev/v1alpha1 29 | kind: Agent 30 | metadata: 31 | name: anthropic-agent 32 | namespace: test 33 | spec: 34 | description: An agent using Anthropic Claude 35 | systemMessage: You are Claude, an AI assistant created by Anthropic. 36 | modelConfig: anthropic-model 37 | tools: [] -------------------------------------------------------------------------------- /go/controller/internal/autogen/testdata/inputs/basic_agent.yaml: -------------------------------------------------------------------------------- 1 | operation: translateAgent 2 | targetObject: basic-agent 3 | namespace: test 4 | objects: 5 | - apiVersion: v1 6 | kind: Secret 7 | metadata: 8 | name: openai-secret 9 | namespace: test 10 | data: 11 | api-key: c2stdGVzdC1hcGkta2V5 # base64 encoded "sk-test-api-key" 12 | - apiVersion: kagent.dev/v1alpha1 13 | kind: ModelConfig 14 | metadata: 15 | name: basic-model 16 | namespace: test 17 | spec: 18 | provider: OpenAI 19 | model: gpt-4o 20 | apiKeySecretRef: openai-secret 21 | apiKeySecretKey: api-key 22 | openAI: 23 | temperature: "0.7" 24 | maxTokens: 1024 25 | topP: "0.95" 26 | - apiVersion: kagent.dev/v1alpha1 27 | kind: Agent 28 | metadata: 29 | name: basic-agent 30 | namespace: test 31 | spec: 32 | description: A basic test agent 33 | systemMessage: You are a helpful assistant. 34 | modelConfig: basic-model 35 | tools: [] -------------------------------------------------------------------------------- /go/controller/internal/autogen/testdata/inputs/ollama_agent.yaml: -------------------------------------------------------------------------------- 1 | operation: translateAgent 2 | targetObject: ollama-agent 3 | namespace: test 4 | objects: 5 | - apiVersion: kagent.dev/v1alpha1 6 | kind: ModelConfig 7 | metadata: 8 | name: ollama-model 9 | namespace: test 10 | spec: 11 | provider: Ollama 12 | model: llama3.2:latest 13 | # No API key needed for Ollama 14 | ollama: 15 | host: "http://localhost:11434" 16 | options: 17 | temperature: "0.8" 18 | top_p: "0.9" 19 | num_ctx: "2048" 20 | modelInfo: 21 | functionCalling: false 22 | jsonOutput: false 23 | family: "llama" 24 | defaultHeaders: 25 | User-Agent: "kagent/1.0" 26 | - apiVersion: kagent.dev/v1alpha1 27 | kind: Agent 28 | metadata: 29 | name: ollama-agent 30 | namespace: test 31 | spec: 32 | description: An agent using Ollama local model 33 | systemMessage: You are a helpful AI assistant running locally via Ollama. 34 | modelConfig: ollama-model 35 | tools: [] -------------------------------------------------------------------------------- /go/controller/internal/httpserver/errors/errors.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | // APIError represents an API error with HTTP status code and message 9 | type APIError struct { 10 | Code int 11 | Message string 12 | Err error 13 | } 14 | 15 | // Error implements the error interface 16 | func (e *APIError) Error() string { 17 | if e.Err != nil { 18 | return fmt.Sprintf("%s: %v", e.Message, e.Err) 19 | } 20 | return e.Message 21 | } 22 | 23 | // Unwrap returns the wrapped error 24 | func (e *APIError) Unwrap() error { 25 | return e.Err 26 | } 27 | 28 | // StatusCode returns the HTTP status code 29 | func (e *APIError) StatusCode() int { 30 | return e.Code 31 | } 32 | 33 | // NewBadRequestError creates a new bad request error 34 | func NewBadRequestError(message string, err error) *APIError { 35 | return &APIError{ 36 | Code: http.StatusBadRequest, 37 | Message: message, 38 | Err: err, 39 | } 40 | } 41 | 42 | // NewNotFoundError creates a new not found error 43 | func NewNotFoundError(message string, err error) *APIError { 44 | return &APIError{ 45 | Code: http.StatusNotFound, 46 | Message: message, 47 | Err: err, 48 | } 49 | } 50 | 51 | // NewInternalServerError creates a new internal server error 52 | func NewInternalServerError(message string, err error) *APIError { 53 | return &APIError{ 54 | Code: http.StatusInternalServerError, 55 | Message: message, 56 | Err: err, 57 | } 58 | } 59 | 60 | // NewValidationError creates a new validation error 61 | func NewValidationError(message string, err error) *APIError { 62 | return &APIError{ 63 | Code: http.StatusUnprocessableEntity, 64 | Message: message, 65 | Err: err, 66 | } 67 | } 68 | 69 | func NewConflictError(message string, err error) *APIError { 70 | return &APIError{ 71 | Code: http.StatusConflict, 72 | Message: message, 73 | Err: err, 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /go/controller/internal/httpserver/handlers/handlers.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/types" 5 | "sigs.k8s.io/controller-runtime/pkg/client" 6 | 7 | autogen_client "github.com/kagent-dev/kagent/go/autogen/client" 8 | ) 9 | 10 | // Handlers holds all the HTTP handler components 11 | type Handlers struct { 12 | Health *HealthHandler 13 | ModelConfig *ModelConfigHandler 14 | Model *ModelHandler 15 | Provider *ProviderHandler 16 | Sessions *SessionsHandler 17 | Teams *TeamsHandler 18 | Tools *ToolsHandler 19 | ToolServers *ToolServersHandler 20 | Invoke *InvokeHandler 21 | Memory *MemoryHandler 22 | Feedback *FeedbackHandler 23 | } 24 | 25 | // Base holds common dependencies for all handlers 26 | type Base struct { 27 | KubeClient client.Client 28 | AutogenClient autogen_client.Client 29 | DefaultModelConfig types.NamespacedName 30 | } 31 | 32 | // NewHandlers creates a new Handlers instance with all handler components 33 | func NewHandlers(kubeClient client.Client, autogenClient autogen_client.Client, defaultModelConfig types.NamespacedName) *Handlers { 34 | base := &Base{ 35 | KubeClient: kubeClient, 36 | AutogenClient: autogenClient, 37 | DefaultModelConfig: defaultModelConfig, 38 | } 39 | 40 | return &Handlers{ 41 | Health: NewHealthHandler(), 42 | ModelConfig: NewModelConfigHandler(base), 43 | Model: NewModelHandler(base), 44 | Provider: NewProviderHandler(base), 45 | Sessions: NewSessionsHandler(base), 46 | Teams: NewTeamsHandler(base), 47 | Tools: NewToolsHandler(base), 48 | ToolServers: NewToolServersHandler(base), 49 | Invoke: NewInvokeHandler(base), 50 | Memory: NewMemoryHandler(base), 51 | Feedback: NewFeedbackHandler(base), 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /go/controller/internal/httpserver/handlers/health.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "net/http" 5 | 6 | ctrllog "sigs.k8s.io/controller-runtime/pkg/log" 7 | ) 8 | 9 | // HealthHandler handles health check requests 10 | type HealthHandler struct{} 11 | 12 | // NewHealthHandler creates a new HealthHandler 13 | func NewHealthHandler() *HealthHandler { 14 | return &HealthHandler{} 15 | } 16 | 17 | // HandleHealth handles GET /health requests 18 | func (h *HealthHandler) HandleHealth(w http.ResponseWriter, r *http.Request) { 19 | log := ctrllog.FromContext(r.Context()).WithName("health-handler") 20 | log.V(1).Info("Handling health check request") 21 | 22 | w.WriteHeader(http.StatusOK) 23 | w.Write([]byte("OK")) 24 | } 25 | -------------------------------------------------------------------------------- /go/controller/internal/httpserver/handlers/mock_client_test.go: -------------------------------------------------------------------------------- 1 | package handlers_test 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | 7 | "github.com/kagent-dev/kagent/go/controller/internal/httpserver/handlers" 8 | ) 9 | 10 | type mockErrorResponseWriter struct { 11 | *httptest.ResponseRecorder 12 | errorReceived error 13 | } 14 | 15 | func newMockErrorResponseWriter() *mockErrorResponseWriter { 16 | return &mockErrorResponseWriter{ 17 | ResponseRecorder: httptest.NewRecorder(), 18 | } 19 | } 20 | 21 | func (m *mockErrorResponseWriter) RespondWithError(err error) { 22 | m.errorReceived = err 23 | 24 | if errWithStatus, ok := err.(interface{ StatusCode() int }); ok { 25 | handlers.RespondWithError(m, errWithStatus.StatusCode(), err.Error()) 26 | } else { 27 | handlers.RespondWithError(m, http.StatusInternalServerError, err.Error()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /go/controller/internal/httpserver/handlers/models.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/kagent-dev/kagent/go/controller/internal/httpserver/errors" 7 | ctrllog "sigs.k8s.io/controller-runtime/pkg/log" 8 | ) 9 | 10 | // ModelHandler handles model requests 11 | type ModelHandler struct { 12 | *Base 13 | } 14 | 15 | // NewModelHandler creates a new ModelHandler 16 | func NewModelHandler(base *Base) *ModelHandler { 17 | return &ModelHandler{Base: base} 18 | } 19 | 20 | func (h *ModelHandler) HandleListSupportedModels(w ErrorResponseWriter, r *http.Request) { 21 | log := ctrllog.FromContext(r.Context()).WithName("model-handler").WithValues("operation", "list-supported-models") 22 | 23 | log.Info("Listing supported models") 24 | 25 | models, err := h.AutogenClient.ListSupportedModels() 26 | if err != nil { 27 | w.RespondWithError(errors.NewInternalServerError("Failed to list supported models", err)) 28 | return 29 | } 30 | 31 | RespondWithJSON(w, http.StatusOK, models) 32 | } 33 | -------------------------------------------------------------------------------- /go/controller/internal/httpserver/helpers.go: -------------------------------------------------------------------------------- 1 | package httpserver 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | ) 8 | 9 | // Common HTTP response helpers 10 | func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) { 11 | response, err := json.Marshal(payload) 12 | if err != nil { 13 | respondWithError(w, http.StatusInternalServerError, "Error marshalling JSON response") 14 | return 15 | } 16 | 17 | w.Header().Set("Content-Type", "application/json") 18 | w.WriteHeader(code) 19 | w.Write(response) 20 | } 21 | 22 | func respondWithError(w http.ResponseWriter, code int, message string) { 23 | respondWithJSON(w, code, map[string]string{"error": message}) 24 | } 25 | 26 | func getUserID(r *http.Request) (string, error) { 27 | userID := r.URL.Query().Get("user_id") 28 | if userID == "" { 29 | return "", fmt.Errorf("user_id is required") 30 | } 31 | return userID, nil 32 | } 33 | -------------------------------------------------------------------------------- /go/controller/internal/httpserver/middleware_error.go: -------------------------------------------------------------------------------- 1 | package httpserver 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | 7 | "github.com/kagent-dev/kagent/go/controller/internal/httpserver/errors" 8 | "github.com/kagent-dev/kagent/go/controller/internal/httpserver/handlers" 9 | ctrllog "sigs.k8s.io/controller-runtime/pkg/log" 10 | ) 11 | 12 | func errorHandlerMiddleware(next http.Handler) http.Handler { 13 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 14 | ew := &errorResponseWriter{ 15 | ResponseWriter: w, 16 | request: r, 17 | } 18 | 19 | next.ServeHTTP(ew, r) 20 | }) 21 | } 22 | 23 | type errorResponseWriter struct { 24 | http.ResponseWriter 25 | request *http.Request 26 | } 27 | 28 | var _ handlers.ErrorResponseWriter = &errorResponseWriter{} 29 | 30 | func (w *errorResponseWriter) RespondWithError(err error) { 31 | log := ctrllog.FromContext(w.request.Context()) 32 | 33 | statusCode := http.StatusInternalServerError 34 | message := "Internal server error" 35 | detail := "" 36 | 37 | if apiErr, ok := err.(*errors.APIError); ok { 38 | statusCode = apiErr.Code 39 | message = apiErr.Message 40 | if apiErr.Err != nil { 41 | detail = apiErr.Err.Error() 42 | log.Error(apiErr.Err, message) 43 | } else { 44 | log.Info(message) 45 | } 46 | } else { 47 | detail = err.Error() 48 | log.Error(err, "Unhandled error") 49 | } 50 | 51 | responseMessage := message 52 | if detail != "" { 53 | responseMessage = message + ": " + detail 54 | } 55 | 56 | w.Header().Set("Content-Type", "application/json") 57 | w.WriteHeader(statusCode) 58 | json.NewEncoder(w).Encode(map[string]string{"error": responseMessage}) 59 | } 60 | -------------------------------------------------------------------------------- /go/controller/internal/utils/common.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import "os" 4 | 5 | func GetResourceNamespace() string { 6 | if val := os.Getenv("KAGENT_NAMESPACE"); val != "" { 7 | return val 8 | } 9 | return "kagent" 10 | } 11 | 12 | func GetGlobalUserID() string { 13 | if val := os.Getenv("KAGENT_GLOBAL_USER_ID"); val != "" { 14 | return val 15 | } 16 | return "admin@kagent.dev" 17 | } 18 | 19 | // MakePtr is a helper function to create a pointer to a value. 20 | func MakePtr[T any](v T) *T { 21 | return &v 22 | } 23 | -------------------------------------------------------------------------------- /go/controller/utils/a2autils/a2a_utils.go: -------------------------------------------------------------------------------- 1 | package a2autils 2 | 3 | import "trpc.group/trpc-go/trpc-a2a-go/protocol" 4 | 5 | // ExtractText extracts the text content from a message. 6 | func ExtractText(message protocol.Message) string { 7 | for _, part := range message.Parts { 8 | if textPart, ok := part.(protocol.TextPart); ok { 9 | return textPart.Text 10 | } 11 | } 12 | return "" 13 | } 14 | -------------------------------------------------------------------------------- /go/test/e2e/manifests/gpt-model-config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kagent.dev/v1alpha1 2 | kind: ModelConfig 3 | metadata: 4 | creationTimestamp: null 5 | name: gpt-model-config 6 | namespace: team-ns 7 | spec: 8 | apiKeySecretKey: apikey 9 | apiKeySecretRef: openai-api-key-secret 10 | model: gpt-4o 11 | openAI: 12 | maxTokens: 2048 13 | temperature: "0.7" 14 | topP: "0.95" 15 | provider: OpenAI 16 | status: 17 | conditions: null 18 | observedGeneration: 0 19 | --- 20 | -------------------------------------------------------------------------------- /helm/.gitignore: -------------------------------------------------------------------------------- 1 | Chart.yaml 2 | Chart.lock 3 | -------------------------------------------------------------------------------- /helm/agents/argo-rollouts/Chart-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: argo-rollouts-agent 3 | description: A Argo Rollouts Agent for kagent 4 | type: application 5 | version: ${VERSION} -------------------------------------------------------------------------------- /helm/agents/argo-rollouts/values.yaml: -------------------------------------------------------------------------------- 1 | modelConfigRef: "" -------------------------------------------------------------------------------- /helm/agents/cilium-crd/Chart-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: cilium-crd-agent 3 | description: A Cilium CRD Agent for kagent 4 | type: application 5 | version: ${VERSION} -------------------------------------------------------------------------------- /helm/agents/cilium-crd/templates/rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: {{ include "kagent.fullname" . }}-cilium-crd-role 5 | labels: 6 | {{- include "kagent.labels" . | nindent 4 }} 7 | rules: 8 | - apiGroups: 9 | - 'cilium.io' 10 | resources: 11 | - '*' 12 | verbs: 13 | - "*" 14 | --- 15 | apiVersion: rbac.authorization.k8s.io/v1 16 | kind: ClusterRoleBinding 17 | metadata: 18 | name: {{ include "kagent.fullname" . }}-cilium-crd-rolebinding 19 | labels: 20 | {{- include "kagent.labels" . | nindent 4 }} 21 | roleRef: 22 | apiGroup: rbac.authorization.k8s.io 23 | kind: ClusterRole 24 | name: {{ include "kagent.fullname" . }}-cilium-crd-role 25 | subjects: 26 | - kind: ServiceAccount 27 | name: {{ include "kagent.fullname" . }} 28 | namespace: {{ include "kagent.namespace" . }} -------------------------------------------------------------------------------- /helm/agents/cilium-crd/values.yaml: -------------------------------------------------------------------------------- 1 | modelConfigRef: "" -------------------------------------------------------------------------------- /helm/agents/helm/Chart-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: helm-agent 3 | description: A Helm Agent for kagent 4 | type: application 5 | version: ${VERSION} -------------------------------------------------------------------------------- /helm/agents/helm/values.yaml: -------------------------------------------------------------------------------- 1 | modelConfigRef: "" -------------------------------------------------------------------------------- /helm/agents/istio/Chart-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: istio-agent 3 | description: A Istio Agent for kagent 4 | type: application 5 | version: ${VERSION} -------------------------------------------------------------------------------- /helm/agents/istio/values.yaml: -------------------------------------------------------------------------------- 1 | modelConfigRef: "" -------------------------------------------------------------------------------- /helm/agents/k8s/Chart-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: k8s-agent 3 | description: A Kubernetes Agent for kagent 4 | type: application 5 | version: ${VERSION} -------------------------------------------------------------------------------- /helm/agents/k8s/values.yaml: -------------------------------------------------------------------------------- 1 | modelConfigRef: "" -------------------------------------------------------------------------------- /helm/agents/kgateway/Chart-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: kgateway-agent 3 | description: A kgateway Agent for kagent 4 | type: application 5 | version: ${VERSION} -------------------------------------------------------------------------------- /helm/agents/kgateway/values.yaml: -------------------------------------------------------------------------------- 1 | modelConfigRef: "" -------------------------------------------------------------------------------- /helm/agents/observability/Chart-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: observability-agent 3 | description: A Observability Agent for kagent 4 | type: application 5 | version: ${VERSION} -------------------------------------------------------------------------------- /helm/agents/observability/values.yaml: -------------------------------------------------------------------------------- 1 | modelConfigRef: "" 2 | prometheus: 3 | url: "prometheus.kagent:9090" 4 | username: "" 5 | password: "" 6 | grafana: 7 | url: "grafana.kagent:3000" 8 | username: "" 9 | password: "" 10 | apiKey: "" 11 | -------------------------------------------------------------------------------- /helm/agents/promql/Chart-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: promql-agent 3 | description: A PromQL Agent for kagent 4 | type: application 5 | version: ${VERSION} -------------------------------------------------------------------------------- /helm/agents/promql/values.yaml: -------------------------------------------------------------------------------- 1 | modelConfigRef: "" -------------------------------------------------------------------------------- /helm/kagent-crds/Chart-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: kagent-crds 3 | description: CRDs for kagent 4 | type: application 5 | version: ${VERSION} -------------------------------------------------------------------------------- /helm/kagent-crds/values.yaml: -------------------------------------------------------------------------------- 1 | # Values for the kagent-crds chart 2 | -------------------------------------------------------------------------------- /helm/kagent/Chart-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: kagent 3 | description: A Helm chart for kagent, built with Autogen 4 | type: application 5 | version: ${VERSION} 6 | dependencies: 7 | - name: k8s-agent 8 | version: ${VERSION} 9 | repository: file://../agents/k8s 10 | condition: k8s-agent.enabled 11 | - name: kgateway-agent 12 | version: ${VERSION} 13 | repository: file://../agents/kgateway 14 | condition: kgateway-agent.enabled 15 | - name: istio-agent 16 | version: ${VERSION} 17 | repository: file://../agents/istio 18 | condition: istio-agent.enabled 19 | - name: promql-agent 20 | version: ${VERSION} 21 | repository: file://../agents/promql 22 | condition: promql-agent.enabled 23 | - name: observability-agent 24 | version: ${VERSION} 25 | repository: file://../agents/observability 26 | condition: observability-agent.enabled 27 | - name: argo-rollouts-agent 28 | version: ${VERSION} 29 | repository: file://../agents/argo-rollouts 30 | condition: argo-rollouts-agent.enabled 31 | - name: helm-agent 32 | version: ${VERSION} 33 | repository: file://../agents/helm 34 | condition: helm-agent.enabled 35 | - name: cilium-crd-agent 36 | version: ${VERSION} 37 | repository: file://../agents/cilium-crd 38 | condition: cilium-crd-agent.enabled 39 | -------------------------------------------------------------------------------- /helm/kagent/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | ################################ To open kagent UI: ########################################### 2 | # 3 | # This is a Helm chart for Kagent, a Kubernetes agent. 4 | # 5 | # 1. Forward application port by running these commands in the terminal: 6 | # kubectl -n {{ include "kagent.namespace" . }} port-forward service/{{ .Release.Name }} 8001:80 7 | # 8 | # 2. Then visit http://127.0.0.1:8001 to use the application. 9 | # 10 | ############################################################################################### -------------------------------------------------------------------------------- /helm/kagent/templates/clusterrolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: {{ include "kagent.fullname" . }}-getter-rolebinding 5 | labels: 6 | {{- include "kagent.labels" . | nindent 4 }} 7 | roleRef: 8 | apiGroup: rbac.authorization.k8s.io 9 | kind: ClusterRole 10 | name: {{ include "kagent.fullname" . }}-getter-role 11 | subjects: 12 | - kind: ServiceAccount 13 | name: {{ include "kagent.fullname" . }} 14 | namespace: {{ include "kagent.namespace" . }} 15 | --- 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | kind: ClusterRoleBinding 18 | metadata: 19 | name: {{ include "kagent.fullname" . }}-writer-rolebinding 20 | labels: 21 | {{- include "kagent.labels" . | nindent 4 }} 22 | roleRef: 23 | apiGroup: rbac.authorization.k8s.io 24 | kind: ClusterRole 25 | name: {{ include "kagent.fullname" . }}-writer-role 26 | subjects: 27 | - kind: ServiceAccount 28 | name: {{ include "kagent.fullname" . }} 29 | namespace: {{ include "kagent.namespace" . }} -------------------------------------------------------------------------------- /helm/kagent/templates/modelconfig.yaml: -------------------------------------------------------------------------------- 1 | {{- $dot := . }} 2 | {{- $defaultProfider := .Values.providers.default | default "openAI" }} 3 | {{- $model := index .Values.providers $defaultProfider }} 4 | {{- if hasKey .Values.providers $defaultProfider | not }} 5 | {{- fail (printf "Provider key=%s is not found under .Values.providers" $defaultProfider) }} 6 | {{- end }} 7 | --- 8 | apiVersion: kagent.dev/v1alpha1 9 | kind: ModelConfig 10 | metadata: 11 | name: {{ include "kagent.defaultModelConfigName" $dot | quote }} 12 | namespace: {{ include "kagent.namespace" . }} 13 | labels: 14 | {{- include "kagent.labels" $dot | nindent 4 }} 15 | spec: 16 | {{- with $model }} 17 | provider: {{ .provider | quote }} 18 | model: {{ .model | quote }} 19 | {{- if $model.apiKeySecretRef }} 20 | apiKeySecretRef: {{.apiKeySecretRef}} 21 | {{- end }} 22 | {{- if $model.apiKeySecretKey }} 23 | apiKeySecretKey: {{.apiKeySecretKey}} 24 | {{- end }} 25 | {{- if hasKey $model "defaultHeaders" }} 26 | defaultHeaders: 27 | {{- toYaml $model.defaultHeaders | nindent 4 }} 28 | {{- end }} 29 | {{ $dot.Values.providers.default }}: 30 | {{- toYaml $model.config | nindent 4 }} 31 | {{- end}} -------------------------------------------------------------------------------- /helm/kagent/templates/secret.yaml: -------------------------------------------------------------------------------- 1 | {{- $dot := . }} 2 | {{- $model := index $dot.Values.providers $dot.Values.providers.default }} 3 | {{- if and $model.apiKeySecretRef $model.apiKey }} 4 | --- 5 | apiVersion: v1 6 | kind: Secret 7 | metadata: 8 | name: {{ $model.apiKeySecretRef | quote }} 9 | namespace: {{ include "kagent.namespace" . }} 10 | labels: 11 | {{- include "kagent.labels" $dot | nindent 4 }} 12 | type: Opaque 13 | data: 14 | {{ $model.apiKeySecretKey | default (printf "%s_API_KEY" $model.provider | upper) }}: {{ $model.apiKey | b64enc }} 15 | {{- end }} -------------------------------------------------------------------------------- /helm/kagent/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "kagent.fullname" . }} 5 | namespace: {{ include "kagent.namespace" . }} 6 | labels: 7 | {{- include "kagent.labels" . | nindent 4 }} 8 | spec: 9 | type: {{ .Values.service.type }} 10 | ports: 11 | - port: {{ .Values.service.ports.ui.port }} 12 | targetPort: {{ .Values.service.ports.ui.targetPort }} 13 | protocol: TCP 14 | name: ui 15 | - port: {{ .Values.service.ports.app.port }} 16 | targetPort: {{ .Values.service.ports.app.targetPort }} 17 | protocol: TCP 18 | name: app 19 | - port: {{ .Values.service.ports.controller.port }} 20 | targetPort: {{ .Values.service.ports.controller.targetPort }} 21 | protocol: TCP 22 | name: controller 23 | selector: 24 | {{- include "kagent.selectorLabels" . | nindent 4 }} 25 | -------------------------------------------------------------------------------- /helm/kagent/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: {{ include "kagent.fullname" . }} 5 | namespace: {{ include "kagent.namespace" . }} 6 | labels: 7 | {{- include "kagent.labels" . | nindent 4 }} -------------------------------------------------------------------------------- /img/arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kagent-dev/kagent/8530aa6251c19d6c0332742740df0f50e2530807/img/arch.png -------------------------------------------------------------------------------- /img/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kagent-dev/kagent/8530aa6251c19d6c0332742740df0f50e2530807/img/hero.png -------------------------------------------------------------------------------- /python/.dockerignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | *.pyo 4 | *.pyd 5 | .Python 6 | env/ 7 | .venv/ 8 | .env 9 | *.log 10 | .git/ 11 | .gitignore 12 | .pytest_cache/ 13 | .coverage 14 | htmlcov/ 15 | workspace/ -------------------------------------------------------------------------------- /python/.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY= -------------------------------------------------------------------------------- /python/.python-version: -------------------------------------------------------------------------------- 1 | 3.12.10 2 | -------------------------------------------------------------------------------- /python/Makefile: -------------------------------------------------------------------------------- 1 | export GIT_LFS_SKIP_SMUDGE=1 2 | 3 | ### Build and Test 4 | 5 | .PHONY: update 6 | update: 7 | uv sync --all-extras --all-groups 8 | uv lock 9 | 10 | .PHONY: format 11 | format: 12 | uv run ruff format 13 | uv run ruff check 14 | 15 | .PHONY: test 16 | test: update 17 | uv run pytest tests 18 | 19 | .PHONY: build 20 | build: update format 21 | -------------------------------------------------------------------------------- /python/README.md: -------------------------------------------------------------------------------- 1 | # kagent 2 | 3 | ## Prerequisites 4 | - [uv package manager](https://docs.astral.sh/uv/getting-started/installation/) 5 | - Open AI API key 6 | 7 | ## Python 8 | 9 | Firstly setup a virtual environment: 10 | ```bash 11 | uv venv .venv 12 | ``` 13 | 14 | We use uv to manage dependencies as well as the python version. 15 | 16 | ```bash 17 | uv python install 3.12 18 | ``` 19 | 20 | Once we have python installed, we can download the dependencies: 21 | 22 | ```bash 23 | uv sync --all-extras 24 | ``` 25 | 26 | ## Running the engine 27 | 28 | ```bash 29 | uv run kagent-engine serve 30 | ``` 31 | 32 | ## Testing 33 | 34 | We use pytest to run tests. 35 | 36 | ```bash 37 | uv run pytest tests/ 38 | ``` 39 | 40 | -------------------------------------------------------------------------------- /python/src/autogenstudio/__init__.py: -------------------------------------------------------------------------------- 1 | from .database.db_manager import DatabaseManager 2 | from .datamodel import Team 3 | from .teammanager import TeamManager 4 | from .version import __version__ 5 | 6 | __all__ = ["DatabaseManager", "Team", "TeamManager", "__version__"] 7 | -------------------------------------------------------------------------------- /python/src/autogenstudio/database/__init__.py: -------------------------------------------------------------------------------- 1 | from .db_manager import DatabaseManager 2 | 3 | __all__ = [ 4 | "DatabaseManager", 5 | ] 6 | -------------------------------------------------------------------------------- /python/src/autogenstudio/datamodel/__init__.py: -------------------------------------------------------------------------------- 1 | from .db import BaseDBModel, Feedback, Gallery, Message, Run, RunStatus, Session, Settings, Team, Tool, ToolServer 2 | from .types import ( 3 | EnvironmentVariable, 4 | GalleryComponents, 5 | GalleryConfig, 6 | GalleryMetadata, 7 | LLMCallEventMessage, 8 | MessageConfig, 9 | MessageMeta, 10 | Response, 11 | SettingsConfig, 12 | SocketMessage, 13 | TeamResult, 14 | ) 15 | 16 | __all__ = [ 17 | "Team", 18 | "Run", 19 | "RunStatus", 20 | "Session", 21 | "Team", 22 | "Message", 23 | "MessageConfig", 24 | "MessageMeta", 25 | "TeamResult", 26 | "Response", 27 | "SocketMessage", 28 | "LLMCallEventMessage", 29 | "Tool", 30 | "GalleryConfig", 31 | "GalleryComponents", 32 | "GalleryMetadata", 33 | "SettingsConfig", 34 | "Settings", 35 | "EnvironmentVariable", 36 | "Gallery", 37 | "ToolServer", 38 | "Feedback", 39 | ] 40 | -------------------------------------------------------------------------------- /python/src/autogenstudio/eval/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kagent-dev/kagent/8530aa6251c19d6c0332742740df0f50e2530807/python/src/autogenstudio/eval/__init__.py -------------------------------------------------------------------------------- /python/src/autogenstudio/gallery/__init__.py: -------------------------------------------------------------------------------- 1 | from .builder import GalleryBuilder, create_default_gallery 2 | 3 | __all__ = ["GalleryBuilder", "create_default_gallery"] 4 | -------------------------------------------------------------------------------- /python/src/autogenstudio/gallery/tools/__init__.py: -------------------------------------------------------------------------------- 1 | from .bing_search import bing_search_tool 2 | from .calculator import calculator_tool 3 | from .fetch_webpage import fetch_webpage_tool 4 | from .generate_image import generate_image_tool 5 | from .google_search import google_search_tool 6 | 7 | __all__ = [ 8 | "bing_search_tool", 9 | "calculator_tool", 10 | "google_search_tool", 11 | "generate_image_tool", 12 | "fetch_webpage_tool", 13 | ] 14 | -------------------------------------------------------------------------------- /python/src/autogenstudio/gallery/tools/calculator.py: -------------------------------------------------------------------------------- 1 | from autogen_core.tools import FunctionTool 2 | 3 | 4 | def calculator(a: float, b: float, operator: str) -> str: 5 | try: 6 | if operator == "+": 7 | return str(a + b) 8 | elif operator == "-": 9 | return str(a - b) 10 | elif operator == "*": 11 | return str(a * b) 12 | elif operator == "/": 13 | if b == 0: 14 | return "Error: Division by zero" 15 | return str(a / b) 16 | else: 17 | return "Error: Invalid operator. Please use +, -, *, or /" 18 | except Exception as e: 19 | return f"Error: {str(e)}" 20 | 21 | 22 | # Create calculator tool 23 | calculator_tool = FunctionTool( 24 | name="calculator", 25 | description="A simple calculator that performs basic arithmetic operations", 26 | func=calculator, 27 | global_imports=[], 28 | ) 29 | -------------------------------------------------------------------------------- /python/src/autogenstudio/sessionmanager/__init__.py: -------------------------------------------------------------------------------- 1 | from .sessionmanager import SessionManager 2 | 3 | __all__ = ["SessionManager"] 4 | -------------------------------------------------------------------------------- /python/src/autogenstudio/teammanager/__init__.py: -------------------------------------------------------------------------------- 1 | from .teammanager import TeamManager 2 | 3 | __all__ = ["TeamManager"] 4 | -------------------------------------------------------------------------------- /python/src/autogenstudio/toolmanager/__init__.py: -------------------------------------------------------------------------------- 1 | from .toolmanager import ToolManager 2 | 3 | __all__ = ["ToolManager"] 4 | -------------------------------------------------------------------------------- /python/src/autogenstudio/toolmanager/toolmanager.py: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | from pathlib import Path 4 | from typing import Union 5 | 6 | import aiofiles 7 | import yaml 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | 12 | class ToolManager: 13 | """Manages loading tool configs from file/folder to populate the DB.""" 14 | 15 | @staticmethod 16 | async def load_from_file(path: Union[str, Path]) -> dict: 17 | """Load tool configuration from JSON/YAML file""" 18 | path = Path(path) 19 | if not path.exists(): 20 | raise FileNotFoundError(f"Config file not found: {path}") 21 | 22 | async with aiofiles.open(path) as f: 23 | content = await f.read() 24 | if path.suffix == ".json": 25 | return json.loads(content) 26 | elif path.suffix in (".yml", ".yaml"): 27 | return yaml.safe_load(content) 28 | raise ValueError(f"Unsupported file format: {path.suffix}") 29 | -------------------------------------------------------------------------------- /python/src/autogenstudio/toolservermanager/__init__.py: -------------------------------------------------------------------------------- 1 | from .toolserver_manager import ToolServerManager 2 | 3 | __all__ = ["ToolServerManager"] 4 | -------------------------------------------------------------------------------- /python/src/autogenstudio/toolservermanager/toolserver_manager.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from autogen_core import Component, ComponentModel 4 | 5 | from kagent.tool_servers import ToolServer 6 | 7 | 8 | class ToolServerManager: 9 | """ToolServerManager manages tool servers and tool discovery from those servers.""" 10 | 11 | async def _create_tool_server( 12 | self, 13 | tool_server_config: Union[dict, ComponentModel], 14 | ) -> ToolServer: 15 | """Create a tool server from the given configuration.""" 16 | if not tool_server_config: 17 | raise Exception("Tool server config is required") 18 | 19 | if isinstance(tool_server_config, dict): 20 | config = tool_server_config 21 | else: 22 | config = tool_server_config.model_dump() 23 | 24 | try: 25 | server = ToolServer.load_component(config) 26 | return server 27 | except Exception as e: 28 | raise Exception(f"Failed to create tool server: {e}") from e 29 | 30 | async def discover_tools(self, tool_server_config: Union[dict, ComponentModel]) -> list[Component]: 31 | """Discover tools from the given tool server.""" 32 | try: 33 | server = await self._create_tool_server(tool_server_config) 34 | return await server.discover_tools() 35 | except Exception as e: 36 | raise Exception(f"Failed to discover tools: {e}") from e 37 | -------------------------------------------------------------------------------- /python/src/autogenstudio/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kagent-dev/kagent/8530aa6251c19d6c0332742740df0f50e2530807/python/src/autogenstudio/utils/__init__.py -------------------------------------------------------------------------------- /python/src/autogenstudio/validation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kagent-dev/kagent/8530aa6251c19d6c0332742740df0f50e2530807/python/src/autogenstudio/validation/__init__.py -------------------------------------------------------------------------------- /python/src/autogenstudio/version.py: -------------------------------------------------------------------------------- 1 | VERSION = "0.4.2" 2 | __version__ = VERSION 3 | APP_NAME = "autogenstudio" 4 | -------------------------------------------------------------------------------- /python/src/autogenstudio/web/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kagent-dev/kagent/8530aa6251c19d6c0332742740df0f50e2530807/python/src/autogenstudio/web/__init__.py -------------------------------------------------------------------------------- /python/src/autogenstudio/web/auth/__init__.py: -------------------------------------------------------------------------------- 1 | from .authroutes import router 2 | from .dependencies import get_current_user, require_admin, require_authenticated, require_roles 3 | from .exceptions import AuthException 4 | from .manager import AuthManager 5 | from .middleware import AuthMiddleware 6 | from .models import AuthConfig, User 7 | from .wsauth import WebSocketAuthHandler 8 | 9 | __all__ = [ 10 | "AuthManager", 11 | "AuthMiddleware", 12 | "AuthConfig", 13 | "User", 14 | "AuthException", 15 | "router", 16 | "get_current_user", 17 | "require_authenticated", 18 | "require_roles", 19 | "require_admin", 20 | "WebSocketAuthHandler", 21 | ] 22 | -------------------------------------------------------------------------------- /python/src/autogenstudio/web/auth/exceptions.py: -------------------------------------------------------------------------------- 1 | from fastapi import HTTPException 2 | 3 | 4 | class AuthException(HTTPException): 5 | """Base class for authentication exceptions.""" 6 | 7 | def __init__(self, detail: str, headers: dict | None = None): 8 | super().__init__(status_code=401, detail=detail, headers=headers) 9 | 10 | 11 | class InvalidTokenException(AuthException): 12 | """Exception raised when token is invalid.""" 13 | 14 | def __init__(self): 15 | super().__init__(detail="Invalid or expired token") 16 | 17 | 18 | class MissingTokenException(AuthException): 19 | """Exception raised when token is missing.""" 20 | 21 | def __init__(self): 22 | super().__init__(detail="Authentication token is missing") 23 | 24 | 25 | class ProviderAuthException(AuthException): 26 | """Exception raised when authentication with provider fails.""" 27 | 28 | def __init__(self, provider: str, detail: str): 29 | super().__init__(detail=f"Authentication failed with {provider}: {detail}") 30 | 31 | 32 | class ConfigurationException(Exception): 33 | """Exception raised when there's an issue with auth configuration.""" 34 | 35 | pass 36 | 37 | 38 | class ForbiddenException(HTTPException): 39 | """Exception raised when user doesn't have permission.""" 40 | 41 | def __init__(self, detail: str = "You don't have permission to access this resource"): 42 | super().__init__(status_code=403, detail=detail) 43 | -------------------------------------------------------------------------------- /python/src/autogenstudio/web/config.py: -------------------------------------------------------------------------------- 1 | # api/config.py 2 | 3 | from pydantic_settings import BaseSettings 4 | 5 | 6 | class Settings(BaseSettings): 7 | DATABASE_URI: str = "sqlite:///./autogen04202.db" 8 | API_DOCS: bool = False 9 | CLEANUP_INTERVAL: int = 300 # 5 minutes 10 | SESSION_TIMEOUT: int = 3600 # 1 hour 11 | CONFIG_DIR: str = "configs" # Default config directory relative to app_root 12 | DEFAULT_USER_ID: str = "admin@kagent.dev" 13 | UPGRADE_DATABASE: bool = False 14 | 15 | model_config = {"env_prefix": "AUTOGENSTUDIO_"} 16 | 17 | 18 | settings = Settings() 19 | -------------------------------------------------------------------------------- /python/src/autogenstudio/web/managers/__init__.py: -------------------------------------------------------------------------------- 1 | # from .connection import WebSocketManager 2 | -------------------------------------------------------------------------------- /python/src/autogenstudio/web/managers/run_context.py: -------------------------------------------------------------------------------- 1 | from contextlib import contextmanager 2 | from contextvars import ContextVar 3 | from typing import Any, ClassVar, Generator 4 | 5 | 6 | class RunContext: 7 | RUN_CONTEXT_VAR: ClassVar[ContextVar] = ContextVar("RUN_CONTEXT_VAR") 8 | 9 | @classmethod 10 | @contextmanager 11 | def populate_context(cls, run_id) -> Generator[None, Any, None]: 12 | token = RunContext.RUN_CONTEXT_VAR.set(run_id) 13 | try: 14 | yield 15 | finally: 16 | RunContext.RUN_CONTEXT_VAR.reset(token) 17 | 18 | @classmethod 19 | def current_run_id(cls) -> str: 20 | try: 21 | return cls.RUN_CONTEXT_VAR.get() 22 | except LookupError as e: 23 | raise RuntimeError("Error getting run id") from e 24 | -------------------------------------------------------------------------------- /python/src/autogenstudio/web/routes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kagent-dev/kagent/8530aa6251c19d6c0332742740df0f50e2530807/python/src/autogenstudio/web/routes/__init__.py -------------------------------------------------------------------------------- /python/src/autogenstudio/web/routes/settingsroute.py: -------------------------------------------------------------------------------- 1 | # api/routes/settings.py 2 | from typing import Dict 3 | 4 | from fastapi import APIRouter, Depends, HTTPException 5 | 6 | from ...datamodel import Settings, SettingsConfig 7 | from ..deps import get_db 8 | 9 | router = APIRouter() 10 | 11 | 12 | @router.get("/") 13 | async def get_settings(user_id: str, db=Depends(get_db)) -> Dict: 14 | try: 15 | response = db.get(Settings, filters={"user_id": user_id}) 16 | if not response.status or not response.data: 17 | # create a default settings 18 | config = SettingsConfig() 19 | default_settings = Settings(user_id=user_id, config=config.model_dump()) 20 | db.upsert(default_settings) 21 | response = db.get(Settings, filters={"user_id": user_id}) 22 | # print(response.data[0]) 23 | return {"status": True, "data": response.data[0]} 24 | except Exception as e: 25 | raise HTTPException(status_code=500, detail=str(e)) from e 26 | 27 | 28 | @router.put("/") 29 | async def update_settings(settings: Settings, db=Depends(get_db)) -> Dict: 30 | response = db.upsert(settings) 31 | if not response.status: 32 | raise HTTPException(status_code=400, detail=response.message) 33 | return {"status": True, "data": response.data} 34 | -------------------------------------------------------------------------------- /python/src/autogenstudio/web/routes/teams.py: -------------------------------------------------------------------------------- 1 | # api/routes/teams.py 2 | from typing import Dict 3 | 4 | from fastapi import APIRouter, Depends, HTTPException 5 | 6 | from ...datamodel import Team 7 | from ...gallery.builder import create_default_gallery 8 | from ..deps import get_db 9 | 10 | router = APIRouter() 11 | 12 | 13 | @router.get("/") 14 | async def list_teams(user_id: str, db=Depends(get_db)) -> Dict: 15 | """List all teams for a user""" 16 | response = db.get(Team, filters={"user_id": user_id}) 17 | 18 | if not response.data or len(response.data) == 0: 19 | default_gallery = create_default_gallery() 20 | default_team = Team(user_id=user_id, component=default_gallery.components.teams[0].model_dump()) 21 | 22 | db.upsert(default_team) 23 | response = db.get(Team, filters={"user_id": user_id}) 24 | 25 | return {"status": True, "data": response.data} 26 | 27 | 28 | @router.get("/{team_id}") 29 | async def get_team(team_id: int, user_id: str, db=Depends(get_db)) -> Dict: 30 | """Get a specific team""" 31 | response = db.get(Team, filters={"id": team_id, "user_id": user_id}) 32 | if not response.status or not response.data: 33 | raise HTTPException(status_code=404, detail="Team not found") 34 | return {"status": True, "data": response.data[0]} 35 | 36 | 37 | @router.post("/") 38 | async def create_team(team: Team, db=Depends(get_db)) -> Dict: 39 | """Create a new team""" 40 | response = db.upsert(team) 41 | if not response.status: 42 | raise HTTPException(status_code=400, detail=response.message) 43 | return {"status": True, "data": response.data} 44 | 45 | 46 | @router.delete("/{team_id}") 47 | async def delete_team(team_id: int, user_id: str, db=Depends(get_db)) -> Dict: 48 | """Delete a team""" 49 | db.delete(filters={"id": team_id, "user_id": user_id}, model_class=Team) 50 | return {"status": True, "message": "Team deleted successfully"} 51 | -------------------------------------------------------------------------------- /python/src/autogenstudio/web/routes/tools.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from fastapi import APIRouter, Depends, HTTPException 4 | 5 | from ...datamodel import Tool 6 | from ..deps import get_db 7 | 8 | router = APIRouter() 9 | 10 | 11 | @router.get("/") 12 | async def list_tools(user_id: str, db=Depends(get_db)) -> Dict: 13 | response = db.get(Tool, filters={"user_id": user_id}) 14 | return {"status": True, "data": response.data} 15 | 16 | 17 | @router.get("/{tool_id}") 18 | async def get_tool(tool_id: int, user_id: str, db=Depends(get_db)) -> Dict: 19 | response = db.get(Tool, filters={"id": tool_id, "user_id": user_id}) 20 | if not response.status or not response.data: 21 | raise HTTPException(status_code=404, detail="Tool not found") 22 | return {"status": True, "data": response.data[0]} 23 | 24 | 25 | @router.post("/") 26 | async def create_tool(tool: Tool, db=Depends(get_db)) -> Dict: 27 | response = db.upsert(tool) 28 | if not response.status: 29 | raise HTTPException(status_code=400, detail=response.message) 30 | return {"status": True, "data": response.data} 31 | 32 | 33 | @router.post("/bulk") 34 | async def create_tools(tools: list[Tool], db=Depends(get_db)) -> Dict: 35 | for tool in tools: 36 | db.upsert(tool) 37 | return {"status": True, "data": tools} 38 | 39 | 40 | @router.delete("/{tool_id}") 41 | async def delete_tool(tool_id: int, user_id: str, db=Depends(get_db)) -> Dict: 42 | db.delete(filters={"id": tool_id, "user_id": user_id}, model_class=Tool) 43 | return {"status": True, "message": "Tool deleted successfully"} 44 | -------------------------------------------------------------------------------- /python/src/autogenstudio/web/routes/validation.py: -------------------------------------------------------------------------------- 1 | # api/routes/validation.py 2 | 3 | from fastapi import APIRouter 4 | 5 | from ...validation.component_test_service import ComponentTestRequest, ComponentTestResult, ComponentTestService 6 | from ...validation.validation_service import ValidationError, ValidationRequest, ValidationResponse, ValidationService 7 | 8 | router = APIRouter() 9 | 10 | 11 | @router.post("/") 12 | async def validate_component(request: ValidationRequest) -> ValidationResponse: 13 | """Validate a component configuration""" 14 | try: 15 | return ValidationService.validate(request.component) 16 | except Exception as e: 17 | return ValidationResponse( 18 | is_valid=False, errors=[ValidationError(field="validation", error=str(e))], warnings=[] 19 | ) 20 | 21 | 22 | @router.post("/test") 23 | async def test_component(request: ComponentTestRequest) -> ComponentTestResult: 24 | """Test a component functionality with appropriate inputs based on type""" 25 | # First validate the component configuration 26 | validation_result = ValidationService.validate(request.component) 27 | 28 | # Only proceed with testing if the component is valid 29 | if not validation_result.is_valid: 30 | return ComponentTestResult( 31 | status=False, message="Component validation failed", logs=[e.error for e in validation_result.errors] 32 | ) 33 | 34 | # If validation passed, run the functional test 35 | return await ComponentTestService.test_component( 36 | component=request.component, timeout=request.timeout if request.timeout else 60 37 | ) 38 | -------------------------------------------------------------------------------- /python/src/kagent/__init__.py: -------------------------------------------------------------------------------- 1 | import importlib.metadata 2 | 3 | ABOUT = "This is kagent." 4 | 5 | __version__ = importlib.metadata.version("kagent") 6 | -------------------------------------------------------------------------------- /python/src/kagent/agents/__init__.py: -------------------------------------------------------------------------------- 1 | from ._task_agent import TaskAgent 2 | 3 | __all__ = ["TaskAgent"] 4 | -------------------------------------------------------------------------------- /python/src/kagent/memory/__init__.py: -------------------------------------------------------------------------------- 1 | from ._pinecone_memory import PineconeMemory 2 | 3 | __all__ = ["PineconeMemory"] 4 | -------------------------------------------------------------------------------- /python/src/kagent/models/vertexai/__init__.py: -------------------------------------------------------------------------------- 1 | from ._gemini_vertexai_client import GeminiVertexAIChatCompletionClient 2 | from ._anthropic_vertex_client import AnthropicVertexAIChatCompletionClient 3 | from .config import GeminiVertexAIClientConfiguration, AnthropicVertexAIClientConfiguration 4 | 5 | __all__ = [ 6 | "GeminiVertexAIChatCompletionClient", 7 | "AnthropicVertexAIChatCompletionClient", 8 | "GeminiVertexAIClientConfiguration", 9 | "AnthropicVertexAIClientConfiguration", 10 | ] 11 | -------------------------------------------------------------------------------- /python/src/kagent/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kagent-dev/kagent/8530aa6251c19d6c0332742740df0f50e2530807/python/src/kagent/py.typed -------------------------------------------------------------------------------- /python/src/kagent/tool_servers/__init__.py: -------------------------------------------------------------------------------- 1 | from ._ssemcptoolserver import SseMcpToolServer, SseMcpToolServerConfig 2 | from ._stdiomcptoolserver import StdioMcpToolServer, StdioMcpToolServerConfig 3 | from ._tool_server import ToolServer 4 | 5 | __all__ = [ 6 | "SseMcpToolServer", 7 | "SseMcpToolServerConfig", 8 | "StdioMcpToolServer", 9 | "StdioMcpToolServerConfig", 10 | "ToolServer", 11 | ] 12 | -------------------------------------------------------------------------------- /python/src/kagent/tool_servers/_ssemcptoolserver.py: -------------------------------------------------------------------------------- 1 | from autogen_core import Component 2 | from autogen_ext.tools.mcp._config import SseServerParams 3 | from autogen_ext.tools.mcp._factory import mcp_server_tools 4 | from loguru import logger 5 | 6 | from ._tool_server import ToolServer 7 | 8 | 9 | class SseMcpToolServerConfig(SseServerParams): 10 | pass 11 | 12 | 13 | class SseMcpToolServer(ToolServer, Component[SseMcpToolServerConfig]): 14 | component_config_schema = SseMcpToolServerConfig 15 | component_type = "tool_server" 16 | component_provider_override = "kagent.tool_servers.SseMcpToolServer" 17 | 18 | def __init__(self, config: SseMcpToolServerConfig): 19 | self.config = config 20 | 21 | async def discover_tools(self) -> list[Component]: 22 | try: 23 | logger.debug(f"Discovering tools from sse server: {self.config}") 24 | tools = await mcp_server_tools(self.config) 25 | return tools 26 | except Exception as e: 27 | raise Exception(f"Failed to discover tools: {e}") from e 28 | 29 | def _to_config(self) -> SseMcpToolServerConfig: 30 | return SseMcpToolServerConfig(**self.config.model_dump()) 31 | 32 | @classmethod 33 | def _from_config(cls, config: SseMcpToolServerConfig): 34 | return cls(config) 35 | -------------------------------------------------------------------------------- /python/src/kagent/tool_servers/_stdiomcptoolserver.py: -------------------------------------------------------------------------------- 1 | from typing import Self 2 | 3 | from autogen_core import Component 4 | from autogen_ext.tools.mcp._config import StdioServerParams 5 | from autogen_ext.tools.mcp._factory import mcp_server_tools 6 | from loguru import logger 7 | 8 | from ._tool_server import ToolServer 9 | 10 | 11 | class StdioMcpToolServerConfig(StdioServerParams): 12 | pass 13 | 14 | 15 | class StdioMcpToolServer(ToolServer, Component[StdioMcpToolServerConfig]): 16 | component_config_schema = StdioMcpToolServerConfig 17 | component_type = "tool_server" 18 | component_provider_override = "kagent.tool_servers.StdioMcpToolServer" 19 | 20 | def __init__(self, config: StdioMcpToolServerConfig): 21 | self.config: StdioMcpToolServerConfig = config 22 | 23 | async def discover_tools(self) -> list[Component]: 24 | try: 25 | logger.debug(f"Discovering tools from stdio server: {self.config}") 26 | tools = await mcp_server_tools(self.config) 27 | return tools 28 | except Exception as e: 29 | raise Exception(f"Failed to discover tools: {e}") from e 30 | 31 | def _to_config(self) -> StdioMcpToolServerConfig: 32 | return StdioMcpToolServerConfig(**self.config.model_dump()) 33 | 34 | @classmethod 35 | def _from_config(cls, config: StdioMcpToolServerConfig) -> Self: 36 | return cls(config) 37 | -------------------------------------------------------------------------------- /python/src/kagent/tool_servers/_tool_server.py: -------------------------------------------------------------------------------- 1 | from abc import ABC 2 | from typing import Protocol 3 | 4 | from autogen_core import Component, ComponentBase 5 | from pydantic import BaseModel 6 | 7 | 8 | class ToolDiscovery(Protocol): 9 | async def discover_tools(self) -> list[Component]: ... 10 | 11 | 12 | class ToolServer(ABC, ToolDiscovery, ComponentBase[BaseModel]): 13 | component_type = "tool_server" 14 | -------------------------------------------------------------------------------- /python/src/kagent/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kagent-dev/kagent/8530aa6251c19d6c0332742740df0f50e2530807/python/src/kagent/tools/__init__.py -------------------------------------------------------------------------------- /python/src/kagent/tools/_utils.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from autogen_core import CancellationToken, Component 4 | from autogen_core.tools import BaseTool, FunctionTool 5 | from pydantic import BaseModel 6 | 7 | 8 | def create_typed_fn_tool(fn_tool: FunctionTool, override_provider: str, class_name: str): 9 | """Creates a concrete typed fn tool class from a function tool.""" 10 | 11 | class ToolConfig(BaseModel): 12 | pass 13 | 14 | class Tool(BaseTool, Component[ToolConfig]): 15 | component_provider_override = override_provider 16 | component_type = "tool" 17 | component_config_schema = ToolConfig 18 | component_description = fn_tool.description 19 | 20 | def __init__(self): 21 | self.fn_tool = fn_tool 22 | super().__init__( 23 | name=fn_tool.name, 24 | description=fn_tool.description, 25 | args_type=fn_tool.args_type(), 26 | return_type=fn_tool.return_type(), 27 | ) 28 | 29 | async def run(self, args: ToolConfig, cancellation_token: CancellationToken) -> Any: 30 | return await self.fn_tool.run(args, cancellation_token) 31 | 32 | def _to_config(self) -> ToolConfig: 33 | return ToolConfig() 34 | 35 | @classmethod 36 | def _from_config(cls, config: ToolConfig): 37 | return cls() 38 | 39 | # Set the class name dynamically 40 | Tool.__name__ = class_name 41 | ToolConfig.__name__ = class_name + "Config" 42 | return (Tool, ToolConfig) 43 | -------------------------------------------------------------------------------- /python/src/kagent/tools/argo/__init__.py: -------------------------------------------------------------------------------- 1 | from ._argo_rollouts_k8sgw_installation import ( 2 | CheckPluginLogsTool, 3 | VerifyGatewayPluginTool, 4 | ) 5 | from ._kubectl_argo_rollouts import ( 6 | PauseRollout, 7 | PromoteRollout, 8 | SetRolloutImage, 9 | VerifyArgoRolloutsControllerInstall, 10 | VerifyKubectlPluginInstall, 11 | ) 12 | 13 | __all__ = [ 14 | "PauseRollout", 15 | "PromoteRollout", 16 | "SetRolloutImage", 17 | "VerifyKubectlPluginInstall", 18 | "VerifyArgoRolloutsControllerInstall", 19 | "CheckPluginLogsTool", 20 | "VerifyGatewayPluginTool", 21 | ] 22 | -------------------------------------------------------------------------------- /python/src/kagent/tools/common/__init__.py: -------------------------------------------------------------------------------- 1 | from ._llm_tool import LLMCallError, LLMTool, LLMToolConfig, LLMToolInput 2 | from ._shell import run_command 3 | 4 | __all__ = ["LLMTool", "LLMToolConfig", "run_command", "LLMCallError", "LLMToolInput"] 5 | -------------------------------------------------------------------------------- /python/src/kagent/tools/common/_shell.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | 4 | # Function that runs the command in the shell 5 | def run_command(command: str, args: list[str]) -> str: 6 | """Run the given command and return the output.""" 7 | try: 8 | output = subprocess.check_output([command] + args, stderr=subprocess.STDOUT) 9 | return output.decode("utf-8") 10 | except subprocess.CalledProcessError as e: 11 | return f"Error running {command} command: {e.output.decode('utf-8')}" 12 | except FileNotFoundError: 13 | return "Error running {command} command: {command} not found in PATH" 14 | -------------------------------------------------------------------------------- /python/src/kagent/tools/datetime/__init__.py: -------------------------------------------------------------------------------- 1 | from ._current_date_time import GetCurrentDateTime 2 | 3 | __all__ = ["GetCurrentDateTime"] 4 | -------------------------------------------------------------------------------- /python/src/kagent/tools/datetime/_current_date_time.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from autogen_core.tools import FunctionTool 4 | 5 | from .._utils import create_typed_fn_tool 6 | 7 | 8 | async def _current_date_time() -> str: 9 | """Returns the current date and time in ISO 8601 format.""" 10 | return datetime.datetime.now().isoformat() 11 | 12 | 13 | current_date_time = FunctionTool( 14 | _current_date_time, 15 | description="Returns the current date and time in ISO 8601 format.", 16 | name="current_date_time", 17 | ) 18 | 19 | GetCurrentDateTime, GetCurrentDateTimeConfig = create_typed_fn_tool( 20 | current_date_time, "kagent.tools.datetime.GetCurrentDateTime", "GetCurrentDateTime" 21 | ) 22 | -------------------------------------------------------------------------------- /python/src/kagent/tools/docs/__init__.py: -------------------------------------------------------------------------------- 1 | from .query_documentation import ( 2 | Config, 3 | QueryTool, 4 | ) 5 | 6 | __all__ = [ 7 | "Config", 8 | "QueryTool", 9 | ] 10 | -------------------------------------------------------------------------------- /python/src/kagent/tools/grafana/__init__.py: -------------------------------------------------------------------------------- 1 | from ._grafana import ( 2 | AlertManagementTool, 3 | AnnotationManagementTool, 4 | Config, 5 | DashboardManagementTool, 6 | DataSourceManagementTool, 7 | FolderManagementTool, 8 | MiscManagementTool, 9 | OrgManagementTool, 10 | TeamManagementTool, 11 | UserManagementTool, 12 | ) 13 | 14 | __all__ = [ 15 | "OrgManagementTool", 16 | "TeamManagementTool", 17 | "AlertManagementTool", 18 | "AnnotationManagementTool", 19 | "MiscManagementTool", 20 | "DashboardManagementTool", 21 | "FolderManagementTool", 22 | "DataSourceManagementTool", 23 | "UserManagementTool", 24 | "Config", 25 | ] 26 | -------------------------------------------------------------------------------- /python/src/kagent/tools/helm/__init__.py: -------------------------------------------------------------------------------- 1 | from ._helm import ( 2 | GetRelease, 3 | GetReleaseConfig, 4 | ListReleases, 5 | ListReleasesConfig, 6 | RepoAdd, 7 | RepoAddConfig, 8 | RepoUpdate, 9 | RepoUpdateConfig, 10 | Uninstall, 11 | UninstallConfig, 12 | Upgrade, 13 | UpgradeConfig, 14 | ) 15 | 16 | __all__ = [ 17 | "GetRelease", 18 | "GetReleaseConfig", 19 | "ListReleases", 20 | "ListReleasesConfig", 21 | "RepoAdd", 22 | "RepoAddConfig", 23 | "RepoUpdate", 24 | "RepoUpdateConfig", 25 | "Uninstall", 26 | "UninstallConfig", 27 | "Upgrade", 28 | "UpgradeConfig", 29 | ] 30 | -------------------------------------------------------------------------------- /python/src/kagent/tools/istio/__init__.py: -------------------------------------------------------------------------------- 1 | from ._istioctl import ( 2 | AnalyzeClusterConfig, 3 | ApplyWaypoint, 4 | DeleteWaypoint, 5 | GenerateManifest, 6 | GenerateWaypoint, 7 | InstallIstio, 8 | ListWaypoints, 9 | ProxyConfig, 10 | ProxyStatus, 11 | RemoteClusters, 12 | Version, 13 | WaypointStatus, 14 | ZTunnelConfig, 15 | ) 16 | 17 | __all__ = [ 18 | "AnalyzeClusterConfig", 19 | "ApplyWaypoint", 20 | "DeleteWaypoint", 21 | "GenerateManifest", 22 | "GenerateWaypoint", 23 | "InstallIstio", 24 | "ListWaypoints", 25 | "ProxyConfig", 26 | "ProxyStatus", 27 | "RemoteClusters", 28 | "Version", 29 | "WaypointStatus", 30 | "ZTunnelConfig", 31 | ] 32 | -------------------------------------------------------------------------------- /python/src/kagent/tools/k8s/__init__.py: -------------------------------------------------------------------------------- 1 | from ._generate_resource import GenerateResourceTool, GenerateResourceToolConfig, GenerateResourceToolInput 2 | from ._kubectl import ( 3 | AnnotateResource, 4 | ApplyManifest, 5 | CheckServiceConnectivity, 6 | CreateResource, 7 | CreateResourceFromUrl, 8 | DeleteResource, 9 | DescribeResource, 10 | ExecuteCommand, 11 | GetAvailableAPIResources, 12 | GetClusterConfiguration, 13 | GetEvents, 14 | GetPodLogs, 15 | GetResources, 16 | GetResourceYAML, 17 | LabelResource, 18 | PatchResource, 19 | RemoveAnnotation, 20 | RemoveLabel, 21 | Rollout, 22 | Scale, 23 | ) 24 | from ._resource_types import ResourceTypes 25 | 26 | __all__ = [ 27 | "AnnotateResource", 28 | "ApplyManifest", 29 | "CheckServiceConnectivity", 30 | "CreateResource", 31 | "CreateResourceFromUrl", 32 | "DeleteResource", 33 | "DescribeResource", 34 | "ExecuteCommand", 35 | "GetAvailableAPIResources", 36 | "GetClusterConfiguration", 37 | "GetEvents", 38 | "GetPodLogs", 39 | "GetResources", 40 | "GetResourceYAML", 41 | "LabelResource", 42 | "PatchResource", 43 | "RemoveAnnotation", 44 | "RemoveLabel", 45 | "Rollout", 46 | "Scale", 47 | "GenerateResourceTool", 48 | "GenerateResourceToolConfig", 49 | "GenerateResourceToolInput", 50 | "ResourceTypes", 51 | ] 52 | -------------------------------------------------------------------------------- /python/src/kagent/tools/k8s/_models.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | from ._resource_types import ResourceTypes 4 | 5 | 6 | class GenerateResourceToolConfig(BaseModel): 7 | model: str = Field(default="gpt-4o-mini", description="The OpenAI model to use for generating the CRD.") 8 | openai_api_key: str = Field( 9 | default="", 10 | description="API key for OpenAI services. If empty, the environment variable 'OPENAI_API_KEY' will be used.", 11 | ) 12 | 13 | 14 | class GenerateResourceToolInput(BaseModel): 15 | resource_description: str = Field(description="Detailed description of the resource to generate YAML for") 16 | resource_type: ResourceTypes = Field(description="Type of resource to generate") 17 | -------------------------------------------------------------------------------- /python/src/kagent/tools/k8s/_prompt_registry.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import Dict 3 | 4 | from ._resource_types import ResourceTypes 5 | from .argo import ANALYSIS_TEMPLATE_PROMPT, ROLLOUT_PROMPT 6 | from .gateway_api import GATEWAY_CLASS_PROMPT, GRPC_ROUTE_PROMPT, HTTP_ROUTE_PROMPT, REFERENCE_GRANT_PROMPT 7 | from .gateway_api import GATEWAY_PROMPT as GWAPI_GATEWAY_PROMPT 8 | from .istio import AUTH_POLICY_PROMPT, GATEWAY_PROMPT, PEER_AUTHENTICATION_PROMPT, VIRTUAL_SERVICE_PROMPT 9 | 10 | logger = logging.getLogger(__name__) 11 | 12 | PROMPT_REGISTRY: Dict[ResourceTypes, str] = { 13 | ResourceTypes.ISTIO_AUTH_POLICY: AUTH_POLICY_PROMPT, 14 | ResourceTypes.ISTIO_GATEWAY: GATEWAY_PROMPT, 15 | ResourceTypes.ISTIO_PEER_AUTHENTICATION: PEER_AUTHENTICATION_PROMPT, 16 | ResourceTypes.ISTIO_VIRTUAL_SERVICE: VIRTUAL_SERVICE_PROMPT, 17 | ResourceTypes.ARGO_ROLLOUT: ROLLOUT_PROMPT, 18 | ResourceTypes.ARGO_ANALYSIS_TEMPLATE: ANALYSIS_TEMPLATE_PROMPT, 19 | ResourceTypes.GWAPI_GATEWAY: GWAPI_GATEWAY_PROMPT, 20 | ResourceTypes.GWAPI_GATEWAY_CLASS: GATEWAY_CLASS_PROMPT, 21 | ResourceTypes.GWAPI_GRPC_ROUTE: GRPC_ROUTE_PROMPT, 22 | ResourceTypes.GWAPI_HTTP_ROUTE: HTTP_ROUTE_PROMPT, 23 | ResourceTypes.GWAPI_REFERENCE_GRANT: REFERENCE_GRANT_PROMPT, 24 | } 25 | 26 | 27 | def get_system_prompt(resource_type: ResourceTypes) -> str: 28 | """ 29 | Retrieve the system prompt for the specified resource type. 30 | 31 | Args: 32 | resource_type: The CRD resource type 33 | 34 | Returns: 35 | The corresponding system prompt 36 | 37 | Raises: 38 | ValueError: If the resource type is not supported 39 | """ 40 | try: 41 | return PROMPT_REGISTRY[resource_type] 42 | except KeyError: 43 | logger.error(f"Unsupported resource type: {resource_type}") 44 | raise ValueError(f"Unsupported resource type: {resource_type}") from KeyError 45 | -------------------------------------------------------------------------------- /python/src/kagent/tools/k8s/_resource_types.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class ResourceTypes(Enum): 5 | # Istio 6 | ISTIO_AUTH_POLICY = "auth_policy" 7 | ISTIO_GATEWAY = "gateway" 8 | ISTIO_PEER_AUTHENTICATION = "peer_authentication" 9 | ISTIO_VIRTUAL_SERVICE = "virtual_service" 10 | 11 | # Argo 12 | ARGO_ROLLOUT = "rollout" 13 | ARGO_ANALYSIS_TEMPLATE = "analysis_template" 14 | 15 | # Gateway API 16 | GWAPI_GATEWAY = "gateway" 17 | GWAPI_GATEWAY_CLASS = "gateway_class" 18 | GWAPI_GRPC_ROUTE = "grpc_route" 19 | GWAPI_HTTP_ROUTE = "http_route" 20 | GWAPI_REFERENCE_GRANT = "reference_grant" 21 | -------------------------------------------------------------------------------- /python/src/kagent/tools/k8s/argo/__init__.py: -------------------------------------------------------------------------------- 1 | from ._analysis_template import ANALYSIS_TEMPLATE_PROMPT 2 | from ._rollout import ROLLOUT_PROMPT 3 | 4 | __all__ = [ 5 | "ANALYSIS_TEMPLATE_PROMPT", 6 | "ROLLOUT_PROMPT", 7 | ] 8 | -------------------------------------------------------------------------------- /python/src/kagent/tools/k8s/gateway_api/__init__.py: -------------------------------------------------------------------------------- 1 | from ._gateway import GATEWAY_PROMPT 2 | from ._gateway_class import GATEWAY_CLASS_PROMPT 3 | from ._grpc_route import GRPC_ROUTE_PROMPT 4 | from ._http_route import HTTP_ROUTE_PROMPT 5 | from ._reference_grant import REFERENCE_GRANT_PROMPT 6 | 7 | __all__ = [ 8 | "GATEWAY_PROMPT", 9 | "GATEWAY_CLASS_PROMPT", 10 | "GRPC_ROUTE_PROMPT", 11 | "HTTP_ROUTE_PROMPT", 12 | "REFERENCE_GRANT_PROMPT", 13 | ] 14 | -------------------------------------------------------------------------------- /python/src/kagent/tools/k8s/istio/__init__.py: -------------------------------------------------------------------------------- 1 | from ._auth_policy import AUTH_POLICY_PROMPT 2 | from ._gateway import GATEWAY_PROMPT 3 | from ._peer_auth import PEER_AUTHENTICATION_PROMPT 4 | from ._virtual_service import VIRTUAL_SERVICE_PROMPT 5 | 6 | __all__ = [ 7 | "AUTH_POLICY_PROMPT", 8 | "GATEWAY_PROMPT", 9 | "PEER_AUTHENTICATION_PROMPT", 10 | "VIRTUAL_SERVICE_PROMPT", 11 | ] 12 | -------------------------------------------------------------------------------- /python/src/kagent/tools/prometheus/__init__.py: -------------------------------------------------------------------------------- 1 | from ._prometheus import ( 2 | AlertmanagersTool, 3 | AlertsTool, 4 | BuildInfoTool, 5 | CleanTombstonesTool, 6 | Config, 7 | CreateSnapshotTool, 8 | DeleteSeriesTool, 9 | LabelNamesTool, 10 | LabelValuesTool, 11 | MetadataTool, 12 | QueryRangeTool, 13 | QueryTool, 14 | RulesTool, 15 | RuntimeInfoTool, 16 | SeriesQueryTool, 17 | StatusConfigTool, 18 | StatusFlagsTool, 19 | TargetMetadataTool, 20 | TargetsTool, 21 | TSDBStatusTool, 22 | WALReplayTool, 23 | ) 24 | from ._promql import GeneratePromQLTool, GeneratePromQLToolConfig 25 | 26 | __all__ = [ 27 | "AlertmanagersTool", 28 | "AlertsTool", 29 | "BuildInfoTool", 30 | "CleanTombstonesTool", 31 | "Config", 32 | "CreateSnapshotTool", 33 | "DeleteSeriesTool", 34 | "LabelNamesTool", 35 | "LabelValuesTool", 36 | "MetadataTool", 37 | "QueryRangeTool", 38 | "QueryTool", 39 | "RulesTool", 40 | "RuntimeInfoTool", 41 | "SeriesQueryTool", 42 | "StatusConfigTool", 43 | "StatusFlagsTool", 44 | "TargetMetadataTool", 45 | "TargetsTool", 46 | "TSDBStatusTool", 47 | "WALReplayTool", 48 | "GeneratePromQLTool", 49 | "GeneratePromQLToolConfig", 50 | ] 51 | -------------------------------------------------------------------------------- /python/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kagent-dev/kagent/8530aa6251c19d6c0332742740df0f50e2530807/python/tests/__init__.py -------------------------------------------------------------------------------- /python/tests/test_load_agents.py: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | import os 4 | from pathlib import Path 5 | 6 | import pytest 7 | from autogen_agentchat.base import Team 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | 12 | # Set up the OpenAI API key as a fixture 13 | @pytest.fixture(scope="module") 14 | def setup_env(): 15 | # Required this be set, but it's unused 16 | os.environ["OPENAI_API_KEY"] = "fake" 17 | 18 | 19 | # Get all agent files 20 | def get_agent_files(): 21 | base_path = Path(__file__).parent.parent / "agents" 22 | files = list(base_path.glob("*.json")) 23 | return files 24 | 25 | 26 | # Create a fixture for each agent file 27 | @pytest.fixture(params=get_agent_files()) 28 | def agent_file(request): 29 | return request.param 30 | 31 | 32 | # Test that loads each agent file individually 33 | @pytest.mark.skip(reason="Skipping agent loading tests") 34 | def test_load_agent(setup_env, agent_file): 35 | with open(agent_file, "r") as f: 36 | agent_config = json.load(f) 37 | Team.load_component(agent_config) 38 | logger.info(f"Successfully loaded agent from {agent_file.name}") 39 | 40 | 41 | # Alternatively, create named fixtures for each agent file 42 | # This allows targeting specific agents in tests if needed 43 | agent_files = get_agent_files() 44 | for file in agent_files: 45 | fixture_name = f"agent_{file.stem}" 46 | globals()[fixture_name] = pytest.fixture(lambda file=file: file) 47 | -------------------------------------------------------------------------------- /ui/.dockerignore: -------------------------------------------------------------------------------- 1 | env/ 2 | *.log 3 | .git/ 4 | .gitignore 5 | .coverage 6 | htmlcov/ 7 | workspace/ 8 | node_modules/ 9 | .next/ -------------------------------------------------------------------------------- /ui/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | -------------------------------------------------------------------------------- /ui/.nvmrc: -------------------------------------------------------------------------------- 1 | lts-hydrogen 2 | -------------------------------------------------------------------------------- /ui/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Install bun 3 | # https://bun.sh/docs/installation 4 | 5 | .PHONY: build 6 | build: 7 | rm -f bun.lockb 8 | bun install 9 | bun run build --no-lint 10 | 11 | .PHONY: clean 12 | clean: 13 | rm -rf node_modules 14 | rm -f bun.lockb 15 | rm -rf dist 16 | rm -rf .bun 17 | rm -rf .cache 18 | 19 | .PHONY: install-bun 20 | install-bun: 21 | brew install bun 22 | 23 | .PHONY: build 24 | update: 25 | @echo "Updating UI lock file..." 26 | bun install --frozen-lockfile --production 27 | bun update --lockfile-only 28 | @echo "Updating UI lock file done." -------------------------------------------------------------------------------- /ui/README.md: -------------------------------------------------------------------------------- 1 | # kagents ui 2 | 3 | 4 | ```bash 5 | # install the dependencies 6 | npm install 7 | 8 | # run the frontend 9 | npm run dev 10 | ``` -------------------------------------------------------------------------------- /ui/bunfig.toml: -------------------------------------------------------------------------------- 1 | [install] 2 | 3 | # equivalent to `--production` flag 4 | production = true 5 | 6 | # whether to install devDependencies 7 | dev = true 8 | 9 | # whether to install optionalDependencies 10 | optional = true 11 | 12 | # whether to install peerDependencies 13 | peer = false 14 | 15 | # equivalent to `--save-text-lockfile` flag 16 | saveTextLockfile = false 17 | 18 | # equivalent to `--frozen-lockfile` flag 19 | frozenLockfile = true -------------------------------------------------------------------------------- /ui/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } -------------------------------------------------------------------------------- /ui/conf/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=root 4 | logfile=/dev/stdout 5 | logfile_maxbytes=0 6 | loglevel=debug 7 | 8 | [program:nginx] 9 | command=/usr/sbin/nginx -g "daemon off;" 10 | autostart=true 11 | autorestart=true 12 | startretries=5 13 | numprocs=1 14 | startsecs=0 15 | priority=20 16 | stdout_logfile=/dev/stdout 17 | stdout_logfile_maxbytes=0 18 | stderr_logfile=/dev/stderr 19 | stderr_logfile_maxbytes=0 20 | 21 | [program:nextjs] 22 | command=bun /app/ui/server.js 23 | directory=/app/ui 24 | user=nextjs 25 | environment=PORT=8001,HOSTNAME="0.0.0.0",NODE_ENV=production,PATH="/usr/local/bun/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 26 | autostart=true 27 | autorestart=true 28 | startretries=5 29 | numprocs=1 30 | startsecs=0 31 | priority=10 32 | stdout_logfile=/dev/stdout 33 | stdout_logfile_maxbytes=0 34 | stderr_logfile=/dev/stderr 35 | stderr_logfile_maxbytes=0 -------------------------------------------------------------------------------- /ui/cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress' 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | baseUrl: 'http://localhost:8001', 6 | setupNodeEvents(on, config) { 7 | }, 8 | supportFile: false 9 | }, 10 | }) -------------------------------------------------------------------------------- /ui/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { dirname } from "path"; 2 | import { fileURLToPath } from "url"; 3 | import { FlatCompat } from "@eslint/eslintrc"; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = dirname(__filename); 7 | 8 | const compat = new FlatCompat({ 9 | baseDirectory: __dirname, 10 | }); 11 | 12 | const eslintConfig = [ 13 | ...compat.extends("next/core-web-vitals", "next/typescript"), 14 | { 15 | files: ["**/*.ts", "**/*.tsx"], 16 | rules: { 17 | "@typescript-eslint/no-unused-vars": ["warn", { "varsIgnorePattern": "_" }] 18 | }, 19 | }, 20 | { 21 | files: ["**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts", "**/*.spec.tsx"], 22 | rules: { 23 | "@typescript-eslint/no-explicit-any": "off", 24 | "@typescript-eslint/no-unused-vars": "off", 25 | "react-hooks/rules-of-hooks": "off", 26 | "react-hooks/exhaustive-deps": "off", 27 | "no-console": "off", 28 | "jest/no-disabled-tests": "off", 29 | "jest/no-focused-tests": "off", 30 | "jest/no-identical-title": "off", 31 | "jest/prefer-to-have-length": "off", 32 | "jest/expect-outside": "off", 33 | "jest/no-conditional-expect": "off", 34 | "jest/no-done-callback": "off", 35 | "jest/no-standalone-expect": "off", 36 | "jest/valid-expect": "off", 37 | "jest/valid-expect-in-promise": "off", 38 | "jest/valid-title": "off", 39 | }, 40 | }, 41 | ]; 42 | 43 | export default eslintConfig; 44 | -------------------------------------------------------------------------------- /ui/jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'jest'; 2 | import nextJest from 'next/jest'; 3 | 4 | const createJestConfig = nextJest({ 5 | // Provide the path to your Next.js app to load next.config.js and .env files in your test environment 6 | dir: './', 7 | }); 8 | 9 | // Add any custom config to be passed to Jest 10 | const config: Config = { 11 | setupFilesAfterEnv: ['/jest.setup.ts'], 12 | testEnvironment: 'jest-environment-jsdom', 13 | moduleNameMapper: { 14 | '^@/(.*)$': '/src/$1', 15 | }, 16 | testMatch: [ 17 | '/src/**/*.test.ts', 18 | '/src/**/*.test.tsx', 19 | ], 20 | collectCoverageFrom: [ 21 | 'src/**/*.{js,jsx,ts,tsx}', 22 | '!src/**/*.d.ts', 23 | '!src/**/*.stories.{js,jsx,ts,tsx}', 24 | '!src/**/*.test.{js,jsx,ts,tsx}', 25 | ], 26 | }; 27 | 28 | // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async 29 | export default createJestConfig(config); -------------------------------------------------------------------------------- /ui/jest.setup.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom'; 2 | 3 | // Mock next/router 4 | jest.mock('next/router', () => ({ 5 | useRouter() { 6 | return { 7 | route: '/', 8 | pathname: '', 9 | query: {}, 10 | asPath: '', 11 | push: jest.fn(), 12 | replace: jest.fn(), 13 | }; 14 | }, 15 | })); 16 | 17 | // Mock next/navigation 18 | jest.mock('next/navigation', () => ({ 19 | useRouter() { 20 | return { 21 | push: jest.fn(), 22 | replace: jest.fn(), 23 | refresh: jest.fn(), 24 | back: jest.fn(), 25 | }; 26 | }, 27 | usePathname() { 28 | return ''; 29 | }, 30 | useSearchParams() { 31 | return new URLSearchParams(); 32 | }, 33 | })); -------------------------------------------------------------------------------- /ui/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | output: "standalone", 5 | logging: { 6 | fetches: { 7 | fullUrl: true, 8 | }, 9 | }, 10 | serverRuntimeConfig: { 11 | trustProxy: true, 12 | }, 13 | }; 14 | 15 | export default nextConfig; 16 | -------------------------------------------------------------------------------- /ui/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /ui/src/app/actions/feedback.ts: -------------------------------------------------------------------------------- 1 | 'use server' 2 | 3 | import { FeedbackData, FeedbackIssueType } from "@/lib/types"; 4 | import { fetchApi, getCurrentUserId } from "./utils"; 5 | 6 | /** 7 | * Submit feedback to the server 8 | */ 9 | async function submitFeedback(feedbackData: FeedbackData): Promise { 10 | const userID = await getCurrentUserId(); 11 | const body = { 12 | is_positive: feedbackData.isPositive, 13 | feedback_text: feedbackData.feedbackText, 14 | issue_type: feedbackData.issueType, 15 | message_id: feedbackData.messageId, 16 | user_id: userID 17 | }; 18 | return await fetchApi('/feedback', { 19 | method: 'POST', 20 | body: JSON.stringify(body), 21 | }); 22 | } 23 | 24 | /** 25 | * Submit positive feedback for an agent response 26 | */ 27 | export async function submitPositiveFeedback( 28 | message_id: number, 29 | feedback_text: string, 30 | ) { 31 | // Create feedback data object 32 | const feedbackData: FeedbackData = { 33 | isPositive: true, 34 | feedbackText: feedback_text, 35 | messageId: message_id, 36 | }; 37 | return await submitFeedback(feedbackData); 38 | } 39 | 40 | /** 41 | * Submit negative feedback for an agent response 42 | */ 43 | export async function submitNegativeFeedback( 44 | message_id: number, 45 | feedback_text: string, 46 | issue_type?: string, 47 | ) { 48 | // Create feedback data object 49 | const feedbackData: FeedbackData = { 50 | isPositive: false, 51 | feedbackText: feedback_text, 52 | issueType: issue_type as FeedbackIssueType, 53 | messageId: message_id, 54 | }; 55 | 56 | return await submitFeedback(feedbackData); 57 | } -------------------------------------------------------------------------------- /ui/src/app/actions/memories.ts: -------------------------------------------------------------------------------- 1 | 'use server' 2 | 3 | import { MemoryResponse, CreateMemoryRequest, UpdateMemoryRequest } from '@/lib/types' 4 | import { fetchApi } from './utils' 5 | 6 | export async function listMemories(): Promise { 7 | const data = await fetchApi('/memories') 8 | return data.map(memory => ({ 9 | ...memory, 10 | memoryParams: memory.memoryParams || {} 11 | })) 12 | } 13 | 14 | export async function getMemory(name: string): Promise { 15 | return fetchApi(`/memories/${name}`) 16 | } 17 | 18 | export async function createMemory( 19 | memoryData: CreateMemoryRequest 20 | ): Promise { 21 | return fetchApi('/memories', { 22 | method: 'POST', 23 | body: JSON.stringify(memoryData), 24 | }) 25 | } 26 | 27 | export async function updateMemory( 28 | memoryData: UpdateMemoryRequest 29 | ): Promise { 30 | return fetchApi(`/memories/${memoryData.name}`, { 31 | method: 'PUT', 32 | body: JSON.stringify(memoryData), 33 | }) 34 | } 35 | 36 | 37 | export async function deleteMemory(name: string): Promise { 38 | await fetchApi(`/memories/${name}`, { 39 | method: 'DELETE', 40 | }) 41 | } -------------------------------------------------------------------------------- /ui/src/app/actions/models.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | import { fetchApi, createErrorResponse } from "./utils"; 3 | import { BaseResponse } from "@/lib/types"; 4 | 5 | 6 | export type ProviderModel = { 7 | name: string; 8 | function_calling: boolean; 9 | } 10 | 11 | // Define the type for the expected API response structure 12 | export type ProviderModelsResponse = Record; 13 | 14 | 15 | /** 16 | * Gets all available models, grouped by provider. 17 | * @returns A promise with all models grouped by provider name. 18 | */ 19 | export async function getModels(): Promise> { 20 | try { 21 | // Update fetchApi to expect the new response type 22 | const response = await fetchApi("/models"); 23 | 24 | if (!response) { 25 | throw new Error("Failed to get models"); 26 | } 27 | 28 | // The data is already in the desired grouped format 29 | return { 30 | success: true, 31 | data: response, 32 | }; 33 | } catch (error) { 34 | // Update createErrorResponse type argument 35 | return createErrorResponse(error, "Error getting model configs"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ui/src/app/actions/providers.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | import { createErrorResponse } from "./utils"; 3 | import { Provider } from "@/lib/types"; 4 | import { BaseResponse } from "@/lib/types"; 5 | import { fetchApi } from "./utils"; 6 | 7 | /** 8 | * Gets the list of supported providers 9 | * @returns A promise with the list of supported providers 10 | */ 11 | export async function getSupportedModelProviders(): Promise> { 12 | try { 13 | const response = await fetchApi("/providers/models"); 14 | 15 | if (!response) { 16 | throw new Error("Failed to get supported model providers"); 17 | } 18 | 19 | return { 20 | success: true, 21 | data: response, 22 | }; 23 | } catch (error) { 24 | return createErrorResponse(error, "Error getting supported providers"); 25 | } 26 | } 27 | 28 | /** 29 | * Gets the list of supported memory providers 30 | * @returns A promise with the list of supported memory providers 31 | */ 32 | export async function getSupportedMemoryProviders(): Promise> { 33 | try { 34 | const response = await fetchApi("/providers/memories"); 35 | 36 | if (!response) { 37 | throw new Error("Failed to get supported memory providers"); 38 | } 39 | 40 | return { 41 | success: true, 42 | data: response, 43 | }; 44 | } catch (error) { 45 | return createErrorResponse(error, "Error getting supported memory providers"); 46 | } 47 | } -------------------------------------------------------------------------------- /ui/src/app/agents/[agentId]/chat/[chatId]/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { use } from "react"; 3 | import ChatInterface from "@/components/chat/ChatInterface"; 4 | 5 | export default function ChatPageView({ params }: { params: Promise<{ agentId: number; chatId: string }> }) { 6 | const { agentId, chatId } = use(params); 7 | 8 | return ; 12 | } 13 | -------------------------------------------------------------------------------- /ui/src/app/agents/[agentId]/chat/page.tsx: -------------------------------------------------------------------------------- 1 | import ChatInterface from "@/components/chat/ChatInterface"; 2 | import { use } from 'react'; 3 | 4 | // This page component receives props (like params) from the Layout 5 | export default function ChatAgentPage({ params }: { params: Promise<{ agentId: number }> }) { 6 | const { agentId } = use(params); 7 | return ; 8 | } -------------------------------------------------------------------------------- /ui/src/app/agents/page.tsx: -------------------------------------------------------------------------------- 1 | import AgentList from "@/components/AgentList"; 2 | export default function AgentListPage() { 3 | return ; 4 | } 5 | -------------------------------------------------------------------------------- /ui/src/app/icon.tsx: -------------------------------------------------------------------------------- 1 | import KagentLogo from '@/components/kagent-logo' 2 | import { ImageResponse } from 'next/og' 3 | 4 | export const size = { 5 | width: 378, 6 | height: 286, 7 | } 8 | export const contentType = 'image/png' 9 | 10 | export default function Icon() { 11 | return new ImageResponse( 12 | ( 13 |
24 | 25 |
26 | ), 27 | { 28 | ...size, 29 | } 30 | ) 31 | } -------------------------------------------------------------------------------- /ui/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Geist } from "next/font/google"; 3 | import "./globals.css"; 4 | import { TooltipProvider } from "@/components/ui/tooltip"; 5 | import { AgentsProvider } from "@/components/AgentsProvider"; 6 | import { Header } from "@/components/Header"; 7 | import { Footer } from "@/components/Footer"; 8 | import { ThemeProvider } from "@/components/ThemeProvider"; 9 | import { Toaster } from "@/components/ui/sonner"; 10 | import { AppInitializer } from "@/components/AppInitializer"; 11 | 12 | const geistSans = Geist({ 13 | variable: "--font-geist-sans", 14 | subsets: ["latin"], 15 | }); 16 | 17 | export const metadata: Metadata = { 18 | title: "kagent.dev | Solo.io", 19 | }; 20 | 21 | export default function RootLayout({ children }: { children: React.ReactNode }) { 22 | return ( 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
{children}
31 |