Skip to content

Commit a827a95

Browse files
supreme-gg-ggEItanya
authored andcommitted
feat: refactor agent skills and support OpenAI Agents SDK (#1100)
Completes #1068 - Integrates OpenAI SDK (fixed event converters, session service, tools, tracing) and added e2e test for OpenAI SDK - Refactor skills prompt and tool logic into `kagent-skills`, update ADK and OpenAI SDK to wrap them with their functions tool implementations --------- Signed-off-by: Eitan Yarmush <[email protected]> Signed-off-by: Jet Chiang <[email protected]> Signed-off-by: Jet Chiang <[email protected]> Co-authored-by: Eitan Yarmush <[email protected]>
1 parent 46391f5 commit a827a95

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+3415
-836
lines changed

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@ push-test-agent: buildx-create build-kagent-adk
168168
$(DOCKER_BUILDER) build --push $(BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -t $(DOCKER_REGISTRY)/kebab:latest -f go/test/e2e/agents/kebab/Dockerfile ./go/test/e2e/agents/kebab
169169
kubectl apply --namespace kagent --context kind-$(KIND_CLUSTER_NAME) -f go/test/e2e/agents/kebab/agent.yaml
170170
$(DOCKER_BUILDER) build --push $(BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -t $(DOCKER_REGISTRY)/poem-flow:latest -f python/samples/crewai/poem_flow/Dockerfile ./python
171-
171+
$(DOCKER_BUILDER) build --push $(BUILD_ARGS) $(TOOLS_IMAGE_BUILD_ARGS) -t $(DOCKER_REGISTRY)/basic-openai:latest -f python/samples/openai/basic_agent/Dockerfile ./python
172+
172173
.PHONY: push-test-skill
173174
push-test-skill: buildx-create
174175
echo "Building FROM DOCKER_REGISTRY=$(DOCKER_REGISTRY)/$(DOCKER_REPO)/kebab-maker:$(VERSION)"

go/go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ require (
1515
github.com/hashicorp/go-multierror v1.1.1
1616
github.com/jedib0t/go-pretty/v6 v6.6.8
1717
github.com/kagent-dev/kmcp v0.2.2
18-
github.com/kagent-dev/mockllm v0.0.2
18+
github.com/kagent-dev/mockllm v0.0.3
1919
github.com/mark3labs/mcp-go v0.40.0
2020
github.com/muesli/reflow v0.3.0
2121
github.com/prometheus/client_golang v1.23.2
@@ -39,7 +39,7 @@ require (
3939

4040
require (
4141
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db // indirect
42-
github.com/anthropics/anthropic-sdk-go v1.13.0 // indirect
42+
github.com/anthropics/anthropic-sdk-go v1.19.0 // indirect
4343
github.com/atotto/clipboard v0.1.4 // indirect
4444
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
4545
github.com/aymanbagabas/go-udiff v0.3.1 // indirect
@@ -60,7 +60,7 @@ require (
6060
github.com/muesli/cancelreader v0.2.2 // indirect
6161
github.com/muesli/termenv v0.16.0 // indirect
6262
github.com/ncruces/go-strftime v0.1.9 // indirect
63-
github.com/openai/openai-go v1.12.0 // indirect
63+
github.com/openai/openai-go/v3 v3.16.0 // indirect
6464
github.com/sahilm/fuzzy v0.1.1 // indirect
6565
github.com/tidwall/gjson v1.18.0 // indirect
6666
github.com/tidwall/match v1.2.0 // indirect

go/go.sum

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ github.com/abiosoft/ishell/v2 v2.0.2 h1:5qVfGiQISaYM8TkbBl7RFO6MddABoXpATrsFbVI+
1212
github.com/abiosoft/ishell/v2 v2.0.2/go.mod h1:E4oTCXfo6QjoCart0QYa5m9w4S+deXs/P/9jA77A9Bs=
1313
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db h1:CjPUSXOiYptLbTdr1RceuZgSFDQ7U15ITERUGrUORx8=
1414
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530=
15-
github.com/anthropics/anthropic-sdk-go v1.13.0 h1:Bhbe8sRoDPtipttg8bQYrMCKe2b79+q6rFW1vOKEUKI=
16-
github.com/anthropics/anthropic-sdk-go v1.13.0/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE=
15+
github.com/anthropics/anthropic-sdk-go v1.19.0 h1:mO6E+ffSzLRvR/YUH9KJC0uGw0uV8GjISIuzem//3KE=
16+
github.com/anthropics/anthropic-sdk-go v1.19.0/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE=
1717
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
1818
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
1919
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
@@ -165,8 +165,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
165165
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
166166
github.com/kagent-dev/kmcp v0.2.2 h1:uvbKmo9IT6OT9RBNXYwGX0PWyxBLfAW1F9yWd5/wxaI=
167167
github.com/kagent-dev/kmcp v0.2.2/go.mod h1:g7wS/3m2wonRo/1DMwVoHxnilr/urPgV2hwV1DwkwrQ=
168-
github.com/kagent-dev/mockllm v0.0.2 h1:xtzXJAacsePYzmzWATxBfd4/AN3Xp27BiGVBPygnl1I=
169-
github.com/kagent-dev/mockllm v0.0.2/go.mod h1:AfO/c+hIYRvO50YfXoYTFcjbiwXxzYFc/efsTcFpoLU=
168+
github.com/kagent-dev/mockllm v0.0.3 h1:hk6Oa/vxHoBrGqRig4GCzox8EqRQYXM4c3oFPP/k9Tg=
169+
github.com/kagent-dev/mockllm v0.0.3/go.mod h1:tDLemRsTZa1NdHaDbg3sgFk9cT1QWvMPlBtLVD6I2mA=
170170
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
171171
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
172172
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
@@ -228,8 +228,8 @@ github.com/onsi/ginkgo/v2 v2.25.3 h1:Ty8+Yi/ayDAGtk4XxmmfUy4GabvM+MegeB4cDLRi6nw
228228
github.com/onsi/ginkgo/v2 v2.25.3/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE=
229229
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
230230
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
231-
github.com/openai/openai-go v1.12.0 h1:NBQCnXzqOTv5wsgNC36PrFEiskGfO5wccfCWDo9S1U0=
232-
github.com/openai/openai-go v1.12.0/go.mod h1:g461MYGXEXBVdV5SaR/5tNzNbSfwTBBefwc+LlDCK0Y=
231+
github.com/openai/openai-go/v3 v3.16.0 h1:VdqS+GFZgAvEOBcWNyvLVwPlYEIboW5xwiUCcLrVf8c=
232+
github.com/openai/openai-go/v3 v3.16.0/go.mod h1:cdufnVK14cWcT9qA1rRtrXx4FTRsgbDPW7Ia7SS5cZo=
233233
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
234234
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
235235
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=

go/test/e2e/invoke_api_test.go

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -501,8 +501,45 @@ func TestE2EInvokeDeclarativeAgentWithMcpServerTool(t *testing.T) {
501501
})
502502
}
503503

504-
// This function generates a CrewAI agent that uses a mock LLM server
505-
// Assumes that the image is built and pushed to registry, the agent can be found in python/samples/crewai/poem_flow
504+
// This function generates an OpenAI BYO agent that uses a mock LLM server
505+
// Assumes that the image is built and pushed to registry
506+
func generateOpenAIAgent(baseURL string) *v1alpha2.Agent {
507+
return &v1alpha2.Agent{
508+
ObjectMeta: metav1.ObjectMeta{
509+
Name: "basic-openai-test-agent",
510+
Namespace: "kagent",
511+
},
512+
Spec: v1alpha2.AgentSpec{
513+
Description: "A basic OpenAI agent with calculator and weather tools",
514+
Type: v1alpha2.AgentType_BYO,
515+
BYO: &v1alpha2.BYOAgentSpec{
516+
Deployment: &v1alpha2.ByoDeploymentSpec{
517+
Image: "localhost:5001/basic-openai:latest",
518+
SharedDeploymentSpec: v1alpha2.SharedDeploymentSpec{
519+
Env: []corev1.EnvVar{
520+
{
521+
Name: "OPENAI_API_KEY",
522+
ValueFrom: &corev1.EnvVarSource{
523+
SecretKeyRef: &corev1.SecretKeySelector{
524+
LocalObjectReference: corev1.LocalObjectReference{
525+
Name: "kagent-openai",
526+
},
527+
Key: "OPENAI_API_KEY",
528+
},
529+
},
530+
},
531+
{
532+
Name: "OPENAI_API_BASE",
533+
Value: baseURL + "/v1",
534+
},
535+
},
536+
},
537+
},
538+
},
539+
},
540+
}
541+
}
542+
506543
func generateCrewAIAgent(baseURL string) *v1alpha2.Agent {
507544
return &v1alpha2.Agent{
508545
ObjectMeta: metav1.ObjectMeta{
@@ -541,6 +578,59 @@ func generateCrewAIAgent(baseURL string) *v1alpha2.Agent {
541578
}
542579
}
543580

581+
func TestE2EInvokeOpenAIAgent(t *testing.T) {
582+
// Setup mock server
583+
baseURL, stopServer := setupMockServer(t, "mocks/invoke_openai_agent.json")
584+
defer stopServer()
585+
586+
// Setup Kubernetes client
587+
cli := setupK8sClient(t, false)
588+
589+
// Setup specific resources
590+
modelCfg := setupModelConfig(t, cli, baseURL)
591+
agent := generateOpenAIAgent(baseURL)
592+
593+
// Create the agent on the cluster
594+
err := cli.Create(t.Context(), agent)
595+
require.NoError(t, err)
596+
597+
// Wait for agent to be ready
598+
args := []string{
599+
"wait",
600+
"--for",
601+
"condition=Ready",
602+
"--timeout=1m",
603+
"agents.kagent.dev",
604+
agent.Name,
605+
"-n",
606+
agent.Namespace,
607+
}
608+
609+
cmd := exec.CommandContext(t.Context(), "kubectl", args...)
610+
cmd.Stdout = os.Stdout
611+
cmd.Stderr = os.Stderr
612+
require.NoError(t, cmd.Run())
613+
614+
defer func() {
615+
cli.Delete(t.Context(), agent) //nolint:errcheck
616+
cli.Delete(t.Context(), modelCfg) //nolint:errcheck
617+
}()
618+
619+
// Setup A2A client - use the agent's actual name
620+
a2aURL := a2aUrl("kagent", "basic-openai-test-agent")
621+
a2aClient, err := a2aclient.NewA2AClient(a2aURL)
622+
require.NoError(t, err)
623+
624+
useArtifacts := true
625+
t.Run("sync_invocation_calculator", func(t *testing.T) {
626+
runSyncTest(t, a2aClient, "What is 2+2?", "4", &useArtifacts)
627+
})
628+
629+
t.Run("streaming_invocation_weather", func(t *testing.T) {
630+
runStreamingTest(t, a2aClient, "What is the weather in London?", "Rainy, 52°F")
631+
})
632+
}
633+
544634
func TestE2EInvokeCrewAIAgent(t *testing.T) {
545635
mockllmCfg, err := mockllm.LoadConfigFromFile("mocks/invoke_crewai_agent.json", mocks)
546636
require.NoError(t, err)
@@ -619,6 +709,8 @@ func TestE2EInvokeCrewAIAgent(t *testing.T) {
619709
t.Run("streaming_invocation", func(t *testing.T) {
620710
runStreamingTest(t, a2aClient, "Generate a poem about CrewAI", "CrewAI is awesome, it makes coding fun.")
621711
})
712+
713+
cli.Delete(t.Context(), agent) //nolint:errcheck
622714
}
623715

624716
func TestE2EInvokeSTSIntegration(t *testing.T) {

go/test/e2e/mocks/invoke_inline_agent.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
{
44
"name": "initial_request",
55
"match": {
6-
"match_type": "exact",
7-
"message" : {
6+
"match_type": "contains",
7+
"message": {
88
"content": "List all nodes in the cluster",
99
"role": "user"
1010
}
@@ -23,6 +23,7 @@
2323
"tool_calls": [
2424
{
2525
"id": "call_1",
26+
"type": "function",
2627
"function": {
2728
"name": "k8s_get_resources",
2829
"arguments": "{\"resource_type\": \"node\", \"all_namespaces\": true}"
@@ -39,7 +40,7 @@
3940
"name": "k8s_get_resources_response",
4041
"match": {
4142
"match_type": "contains",
42-
"message" : {
43+
"message": {
4344
"content": "kagent-control-plane",
4445
"role": "tool",
4546
"tool_call_id": "call_1"
@@ -62,4 +63,4 @@
6263
}
6364
}
6465
]
65-
}
66+
}

go/test/e2e/mocks/invoke_mcp_agent.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
{
44
"name": "initial_request",
55
"match": {
6-
"match_type": "exact",
7-
"message" : {
6+
"match_type": "contains",
7+
"message": {
88
"content": "add 3 and 5",
99
"role": "user"
1010
}
@@ -23,6 +23,7 @@
2323
"tool_calls": [
2424
{
2525
"id": "call_1",
26+
"type": "function",
2627
"function": {
2728
"name": "add",
2829
"arguments": "{\"a\": 3, \"b\": 5}"
@@ -39,7 +40,7 @@
3940
"name": "add_response",
4041
"match": {
4142
"match_type": "contains",
42-
"message" : {
43+
"message": {
4344
"content": "8",
4445
"role": "tool",
4546
"tool_call_id": "call_1"
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
{
2+
"openai": [
3+
{
4+
"name": "calculate_request",
5+
"match": {
6+
"match_type": "contains",
7+
"message": {
8+
"content": "What is 2+2?",
9+
"role": "user"
10+
}
11+
},
12+
"response": {
13+
"id": "chatcmpl-calc",
14+
"object": "chat.completion",
15+
"created": 1677652288,
16+
"model": "gpt-4.1-mini",
17+
"choices": [
18+
{
19+
"index": 0,
20+
"message": {
21+
"role": "assistant",
22+
"content": null,
23+
"tool_calls": [
24+
{
25+
"id": "call_abc123",
26+
"type": "function",
27+
"function": {
28+
"name": "calculate",
29+
"arguments": "{\"expression\": \"2+2\"}"
30+
}
31+
}
32+
]
33+
},
34+
"finish_reason": "tool_calls"
35+
}
36+
]
37+
}
38+
},
39+
{
40+
"name": "calculate_result",
41+
"match": {
42+
"match_type": "contains",
43+
"message": {
44+
"content": "4",
45+
"role": "tool",
46+
"tool_call_id": "call_abc123"
47+
}
48+
},
49+
"response": {
50+
"id": "chatcmpl-calc-result",
51+
"object": "chat.completion",
52+
"created": 1677652288,
53+
"model": "gpt-4.1-mini",
54+
"choices": [
55+
{
56+
"index": 0,
57+
"message": {
58+
"role": "assistant",
59+
"content": "The result of 2+2 is 4"
60+
},
61+
"finish_reason": "stop"
62+
}
63+
]
64+
}
65+
},
66+
{
67+
"name": "weather_request",
68+
"match": {
69+
"match_type": "contains",
70+
"message": {
71+
"content": "What is the weather in London?",
72+
"role": "user"
73+
}
74+
},
75+
"response": {
76+
"id": "chatcmpl-weather",
77+
"object": "chat.completion",
78+
"created": 1677652289,
79+
"model": "gpt-4.1-mini",
80+
"choices": [
81+
{
82+
"index": 0,
83+
"message": {
84+
"role": "assistant",
85+
"content": null,
86+
"tool_calls": [
87+
{
88+
"id": "call_def456",
89+
"type": "function",
90+
"function": {
91+
"name": "get_weather",
92+
"arguments": "{\"location\": \"London\"}"
93+
}
94+
}
95+
]
96+
},
97+
"finish_reason": "tool_calls"
98+
}
99+
]
100+
}
101+
},
102+
{
103+
"name": "weather_result",
104+
"match": {
105+
"match_type": "contains",
106+
"message": {
107+
"content": "Rainy, 52°F",
108+
"role": "tool",
109+
"tool_call_id": "call_def456"
110+
}
111+
},
112+
"response": {
113+
"id": "chatcmpl-weather-result",
114+
"object": "chat.completion",
115+
"created": 1677652289,
116+
"model": "gpt-4.1-mini",
117+
"choices": [
118+
{
119+
"index": 0,
120+
"message": {
121+
"role": "assistant",
122+
"content": "The weather in London is Rainy, 52°F"
123+
},
124+
"finish_reason": "stop"
125+
}
126+
]
127+
}
128+
}
129+
]
130+
}

0 commit comments

Comments
 (0)