kagent-adk 0.6.7__py3-none-any.whl → 0.6.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kagent-adk might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  import importlib.metadata
2
2
 
3
- from .a2a import KAgentApp
3
+ from ._a2a import KAgentApp
4
4
  from .models import AgentConfig
5
5
 
6
6
  __version__ = importlib.metadata.version("kagent_adk")
@@ -1,20 +1,13 @@
1
1
  #! /usr/bin/env python3
2
2
  import faulthandler
3
- import inspect
4
3
  import logging
5
4
  import os
6
- import sys
7
- from contextlib import asynccontextmanager
8
- from typing import Awaitable, Callable, override
5
+ from typing import Callable
9
6
 
10
7
  import httpx
11
- from a2a.auth.user import User
12
- from a2a.server.agent_execution import RequestContext, SimpleRequestContextBuilder
13
- from a2a.server.apps import A2AStarletteApplication
14
- from a2a.server.context import ServerCallContext
8
+ from a2a.server.apps import A2AFastAPIApplication
15
9
  from a2a.server.request_handlers import DefaultRequestHandler
16
- from a2a.server.tasks import TaskStore
17
- from a2a.types import AgentCard, MessageSendParams, Task
10
+ from a2a.types import AgentCard
18
11
  from fastapi import FastAPI, Request
19
12
  from fastapi.responses import PlainTextResponse
20
13
  from google.adk.agents import BaseAgent
@@ -22,54 +15,16 @@ from google.adk.runners import Runner
22
15
  from google.adk.sessions import InMemorySessionService
23
16
  from google.genai import types
24
17
 
18
+ from kagent.core.a2a import KAgentRequestContextBuilder, KAgentTaskStore
19
+
25
20
  from ._agent_executor import A2aAgentExecutor
26
21
  from ._session_service import KAgentSessionService
27
- from ._task_store import KAgentTaskStore
28
22
  from ._token import KAgentTokenService
29
23
 
30
24
  # --- Configure Logging ---
31
25
  logger = logging.getLogger(__name__)
32
26
 
33
27
 
34
- class KAgentUser(User):
35
- def __init__(self, user_id: str):
36
- self.user_id = user_id
37
-
38
- @property
39
- def is_authenticated(self) -> bool:
40
- return True
41
-
42
- @property
43
- def user_name(self) -> str:
44
- return self.user_id
45
-
46
-
47
- class KAgentRequestContextBuilder(SimpleRequestContextBuilder):
48
- """
49
- A request context builder that will be used to hack in the user_id for now.
50
- """
51
-
52
- def __init__(self, task_store: TaskStore):
53
- super().__init__(task_store=task_store)
54
-
55
- async def build(
56
- self,
57
- params: MessageSendParams | None = None,
58
- task_id: str | None = None,
59
- context_id: str | None = None,
60
- task: Task | None = None,
61
- context: ServerCallContext | None = None,
62
- ) -> RequestContext:
63
- if context:
64
- # grab the user id from the header
65
- headers = context.state.get("headers", {})
66
- user_id = headers.get("x-user-id", None)
67
- if user_id:
68
- context.user = KAgentUser(user_id=user_id)
69
- request_context = await super().build(params, task_id, context_id, task, context)
70
- return request_context
71
-
72
-
73
28
  def health_check(request: Request) -> PlainTextResponse:
74
29
  return PlainTextResponse("OK")
75
30
 
@@ -126,7 +81,7 @@ class KAgentApp:
126
81
  request_context_builder=request_context_builder,
127
82
  )
128
83
 
129
- a2a_app = A2AStarletteApplication(
84
+ a2a_app = A2AFastAPIApplication(
130
85
  agent_card=self.agent_card,
131
86
  http_handler=request_handler,
132
87
  )
@@ -13,6 +13,7 @@ from a2a.server.events.event_queue import EventQueue
13
13
  from a2a.types import (
14
14
  Artifact,
15
15
  Message,
16
+ Part,
16
17
  Role,
17
18
  TaskArtifactUpdateEvent,
18
19
  TaskState,
@@ -20,15 +21,16 @@ from a2a.types import (
20
21
  TaskStatusUpdateEvent,
21
22
  TextPart,
22
23
  )
23
- from google.adk.a2a.converters.event_converter import convert_event_to_a2a_events
24
- from google.adk.a2a.converters.request_converter import convert_a2a_request_to_adk_run_args
25
- from google.adk.a2a.converters.utils import _get_adk_metadata_key
26
- from google.adk.a2a.executor.task_result_aggregator import TaskResultAggregator
27
24
  from google.adk.runners import Runner
28
25
  from opentelemetry import trace
29
26
  from pydantic import BaseModel
30
27
  from typing_extensions import override
31
28
 
29
+ from kagent.core.a2a import TaskResultAggregator, get_kagent_metadata_key
30
+
31
+ from .converters.event_converter import convert_event_to_a2a_events
32
+ from .converters.request_converter import convert_a2a_request_to_adk_run_args
33
+
32
34
  logger = logging.getLogger("google_adk." + __name__)
33
35
 
34
36
 
@@ -134,7 +136,7 @@ class A2aAgentExecutor(AgentExecutor):
134
136
  message=Message(
135
137
  message_id=str(uuid.uuid4()),
136
138
  role=Role.agent,
137
- parts=[TextPart(text=str(e))],
139
+ parts=[Part(TextPart(text=str(e)))],
138
140
  ),
139
141
  ),
140
142
  context_id=context.context_id,
@@ -193,9 +195,9 @@ class A2aAgentExecutor(AgentExecutor):
193
195
  context_id=context.context_id,
194
196
  final=False,
195
197
  metadata={
196
- _get_adk_metadata_key("app_name"): runner.app_name,
197
- _get_adk_metadata_key("user_id"): run_args["user_id"],
198
- _get_adk_metadata_key("session_id"): run_args["session_id"],
198
+ get_kagent_metadata_key("app_name"): runner.app_name,
199
+ get_kagent_metadata_key("user_id"): run_args["user_id"],
200
+ get_kagent_metadata_key("session_id"): run_args["session_id"],
199
201
  },
200
202
  )
201
203
  )
@@ -3,6 +3,8 @@ import asyncio
3
3
  from contextlib import asynccontextmanager
4
4
  from typing import Any, Optional
5
5
 
6
+ import httpx
7
+
6
8
  KAGENT_TOKEN_PATH = "/var/run/secrets/tokens/kagent-token"
7
9
  logger = logging.getLogger(__name__)
8
10
 
@@ -59,7 +61,7 @@ class KAgentTokenService:
59
61
  async with self.update_lock:
60
62
  self.token = token
61
63
 
62
- async def _add_bearer_token(self, request):
64
+ async def _add_bearer_token(self, request: httpx.Request):
63
65
  # Your function to generate headers dynamically
64
66
  token = await self._get_token()
65
67
  headers = {"X-Agent-Name": self.app_name}
kagent/adk/cli.py ADDED
@@ -0,0 +1,111 @@
1
+ import asyncio
2
+ import json
3
+ import logging
4
+ import os
5
+ from typing import Annotated
6
+
7
+ import typer
8
+ import uvicorn
9
+ from a2a.types import AgentCard
10
+ from google.adk.cli.utils.agent_loader import AgentLoader
11
+
12
+ from kagent.core import KAgentConfig, configure_tracing
13
+
14
+ from . import AgentConfig, KAgentApp
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ app = typer.Typer()
19
+
20
+
21
+ @app.command()
22
+ def static(
23
+ host: str = "127.0.0.1",
24
+ port: int = 8080,
25
+ workers: int = 1,
26
+ filepath: str = "/config",
27
+ reload: Annotated[bool, typer.Option("--reload")] = False,
28
+ ):
29
+ app_cfg = KAgentConfig()
30
+
31
+ with open(os.path.join(filepath, "config.json"), "r") as f:
32
+ config = json.load(f)
33
+ agent_config = AgentConfig.model_validate(config)
34
+ with open(os.path.join(filepath, "agent-card.json"), "r") as f:
35
+ agent_card = json.load(f)
36
+ agent_card = AgentCard.model_validate(agent_card)
37
+ root_agent = agent_config.to_agent(app_cfg.name)
38
+
39
+ kagent_app = KAgentApp(root_agent, agent_card, app_cfg.url, app_cfg.app_name)
40
+
41
+ server = kagent_app.build()
42
+ configure_tracing(server)
43
+
44
+ uvicorn.run(
45
+ server,
46
+ host=host,
47
+ port=port,
48
+ workers=workers,
49
+ reload=reload,
50
+ )
51
+
52
+
53
+ @app.command()
54
+ def run(
55
+ name: Annotated[str, typer.Argument(help="The name of the agent to run")],
56
+ working_dir: str = ".",
57
+ host: str = "127.0.0.1",
58
+ port: int = 8080,
59
+ workers: int = 1,
60
+ ):
61
+ app_cfg = KAgentConfig()
62
+
63
+ agent_loader = AgentLoader(agents_dir=working_dir)
64
+ root_agent = agent_loader.load_agent(name)
65
+
66
+ with open(os.path.join(working_dir, name, "agent-card.json"), "r") as f:
67
+ agent_card = json.load(f)
68
+ agent_card = AgentCard.model_validate(agent_card)
69
+ kagent_app = KAgentApp(root_agent, agent_card, app_cfg.url, app_cfg.app_name)
70
+ server = kagent_app.build()
71
+ configure_tracing(server)
72
+
73
+ uvicorn.run(
74
+ server,
75
+ host=host,
76
+ port=port,
77
+ workers=workers,
78
+ )
79
+
80
+
81
+ async def test_agent(agent_config: AgentConfig, agent_card: AgentCard, task: str):
82
+ app_cfg = KAgentConfig()
83
+ agent = agent_config.to_agent(app_cfg.name)
84
+ app = KAgentApp(agent, agent_card, app_cfg.url, app_cfg.app_name)
85
+ await app.test(task)
86
+
87
+
88
+ @app.command()
89
+ def test(
90
+ task: Annotated[str, typer.Option("--task", help="The task to test the agent with")],
91
+ filepath: Annotated[str, typer.Option("--filepath", help="The path to the agent config file")],
92
+ ):
93
+ with open(filepath, "r") as f:
94
+ content = f.read()
95
+ config = json.loads(content)
96
+
97
+ with open(os.path.join(filepath, "agent-card.json"), "r") as f:
98
+ agent_card = json.load(f)
99
+ agent_card = AgentCard.model_validate(agent_card)
100
+ agent_config = AgentConfig.model_validate(config)
101
+ asyncio.run(test_agent(agent_config, agent_card, task))
102
+
103
+
104
+ def run_cli():
105
+ logging.basicConfig(level=logging.INFO)
106
+ logging.info("Starting KAgent")
107
+ app()
108
+
109
+
110
+ if __name__ == "__main__":
111
+ run_cli()
File without changes
@@ -0,0 +1,315 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import uuid
5
+ from datetime import datetime, timezone
6
+ from typing import Any, Dict, List, Optional
7
+
8
+ from a2a.server.events import Event as A2AEvent
9
+ from a2a.types import DataPart, Message, Role, Task, TaskState, TaskStatus, TaskStatusUpdateEvent, TextPart
10
+ from a2a.types import Part as A2APart
11
+ from google.adk.agents.invocation_context import InvocationContext
12
+ from google.adk.events.event import Event
13
+ from google.adk.flows.llm_flows.functions import REQUEST_EUC_FUNCTION_CALL_NAME
14
+ from google.genai import types as genai_types
15
+
16
+ from kagent.core.a2a import (
17
+ A2A_DATA_PART_METADATA_IS_LONG_RUNNING_KEY,
18
+ A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL,
19
+ A2A_DATA_PART_METADATA_TYPE_KEY,
20
+ get_kagent_metadata_key,
21
+ )
22
+
23
+ from .part_converter import (
24
+ convert_genai_part_to_a2a_part,
25
+ )
26
+
27
+ # Constants
28
+
29
+ ARTIFACT_ID_SEPARATOR = "-"
30
+ DEFAULT_ERROR_MESSAGE = "An error occurred during processing"
31
+
32
+ # Logger
33
+ logger = logging.getLogger("kagent_adk." + __name__)
34
+
35
+
36
+ def _serialize_metadata_value(value: Any) -> str:
37
+ """Safely serializes metadata values to string format.
38
+
39
+ Args:
40
+ value: The value to serialize.
41
+
42
+ Returns:
43
+ String representation of the value.
44
+ """
45
+ if hasattr(value, "model_dump"):
46
+ try:
47
+ return value.model_dump(exclude_none=True, by_alias=True)
48
+ except Exception as e:
49
+ logger.warning("Failed to serialize metadata value: %s", e)
50
+ return str(value)
51
+ return str(value)
52
+
53
+
54
+ def _get_context_metadata(event: Event, invocation_context: InvocationContext) -> Dict[str, str]:
55
+ """Gets the context metadata for the event.
56
+
57
+ Args:
58
+ event: The ADK event to extract metadata from.
59
+ invocation_context: The invocation context containing session information.
60
+
61
+ Returns:
62
+ A dictionary containing the context metadata.
63
+
64
+ Raises:
65
+ ValueError: If required fields are missing from event or context.
66
+ """
67
+ if not event:
68
+ raise ValueError("Event cannot be None")
69
+ if not invocation_context:
70
+ raise ValueError("Invocation context cannot be None")
71
+
72
+ try:
73
+ metadata = {
74
+ get_kagent_metadata_key("app_name"): invocation_context.app_name,
75
+ get_kagent_metadata_key("user_id"): invocation_context.user_id,
76
+ get_kagent_metadata_key("session_id"): invocation_context.session.id,
77
+ get_kagent_metadata_key("invocation_id"): event.invocation_id,
78
+ get_kagent_metadata_key("author"): event.author,
79
+ }
80
+
81
+ # Add optional metadata fields if present
82
+ optional_fields = [
83
+ ("branch", event.branch),
84
+ ("grounding_metadata", event.grounding_metadata),
85
+ ("custom_metadata", event.custom_metadata),
86
+ ("usage_metadata", event.usage_metadata),
87
+ ("error_code", event.error_code),
88
+ ]
89
+
90
+ for field_name, field_value in optional_fields:
91
+ if field_value is not None:
92
+ metadata[get_kagent_metadata_key(field_name)] = _serialize_metadata_value(field_value)
93
+
94
+ return metadata
95
+
96
+ except Exception as e:
97
+ logger.error("Failed to create context metadata: %s", e)
98
+ raise
99
+
100
+
101
+ def _create_artifact_id(app_name: str, user_id: str, session_id: str, filename: str, version: int) -> str:
102
+ """Creates a unique artifact ID.
103
+
104
+ Args:
105
+ app_name: The application name.
106
+ user_id: The user ID.
107
+ session_id: The session ID.
108
+ filename: The artifact filename.
109
+ version: The artifact version.
110
+
111
+ Returns:
112
+ A unique artifact ID string.
113
+ """
114
+ components = [app_name, user_id, session_id, filename, str(version)]
115
+ return ARTIFACT_ID_SEPARATOR.join(components)
116
+
117
+
118
+ def _process_long_running_tool(a2a_part: A2APart, event: Event) -> None:
119
+ """Processes long-running tool metadata for an A2A part.
120
+
121
+ Args:
122
+ a2a_part: The A2A part to potentially mark as long-running.
123
+ event: The ADK event containing long-running tool information.
124
+ """
125
+ if (
126
+ isinstance(a2a_part.root, DataPart)
127
+ and event.long_running_tool_ids
128
+ and a2a_part.root.metadata
129
+ and a2a_part.root.metadata.get(get_kagent_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY))
130
+ == A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL
131
+ and a2a_part.root.data.get("id") in event.long_running_tool_ids
132
+ ):
133
+ a2a_part.root.metadata[get_kagent_metadata_key(A2A_DATA_PART_METADATA_IS_LONG_RUNNING_KEY)] = True
134
+
135
+
136
+ def convert_event_to_a2a_message(
137
+ event: Event, invocation_context: InvocationContext, role: Role = Role.agent
138
+ ) -> Optional[Message]:
139
+ """Converts an ADK event to an A2A message.
140
+
141
+ Args:
142
+ event: The ADK event to convert.
143
+ invocation_context: The invocation context.
144
+
145
+ Returns:
146
+ An A2A Message if the event has content, None otherwise.
147
+
148
+ Raises:
149
+ ValueError: If required parameters are invalid.
150
+ """
151
+ if not event:
152
+ raise ValueError("Event cannot be None")
153
+ if not invocation_context:
154
+ raise ValueError("Invocation context cannot be None")
155
+
156
+ if not event.content or not event.content.parts:
157
+ return None
158
+
159
+ try:
160
+ a2a_parts = []
161
+ for part in event.content.parts:
162
+ a2a_part = convert_genai_part_to_a2a_part(part)
163
+ if a2a_part:
164
+ a2a_parts.append(a2a_part)
165
+ _process_long_running_tool(a2a_part, event)
166
+
167
+ if a2a_parts:
168
+ return Message(message_id=str(uuid.uuid4()), role=role, parts=a2a_parts)
169
+
170
+ except Exception as e:
171
+ logger.error("Failed to convert event to status message: %s", e)
172
+ raise
173
+
174
+ return None
175
+
176
+
177
+ def _create_error_status_event(
178
+ event: Event,
179
+ invocation_context: InvocationContext,
180
+ task_id: Optional[str] = None,
181
+ context_id: Optional[str] = None,
182
+ ) -> TaskStatusUpdateEvent:
183
+ """Creates a TaskStatusUpdateEvent for error scenarios.
184
+
185
+ Args:
186
+ event: The ADK event containing error information.
187
+ invocation_context: The invocation context.
188
+ task_id: Optional task ID to use for generated events.
189
+ context_id: Optional Context ID to use for generated events.
190
+
191
+ Returns:
192
+ A TaskStatusUpdateEvent with FAILED state.
193
+ """
194
+ error_message = getattr(event, "error_message", None) or DEFAULT_ERROR_MESSAGE
195
+
196
+ # Get context metadata and add error code
197
+ event_metadata = _get_context_metadata(event, invocation_context)
198
+ if event.error_code:
199
+ event_metadata[get_kagent_metadata_key("error_code")] = str(event.error_code)
200
+
201
+ return TaskStatusUpdateEvent(
202
+ task_id=task_id,
203
+ context_id=context_id,
204
+ metadata=event_metadata,
205
+ status=TaskStatus(
206
+ state=TaskState.failed,
207
+ message=Message(
208
+ message_id=str(uuid.uuid4()),
209
+ role=Role.agent,
210
+ parts=[A2APart(TextPart(text=error_message))],
211
+ metadata={get_kagent_metadata_key("error_code"): str(event.error_code)} if event.error_code else {},
212
+ ),
213
+ timestamp=datetime.now(timezone.utc).isoformat(),
214
+ ),
215
+ final=False,
216
+ )
217
+
218
+
219
+ def _create_status_update_event(
220
+ message: Message,
221
+ invocation_context: InvocationContext,
222
+ event: Event,
223
+ task_id: Optional[str] = None,
224
+ context_id: Optional[str] = None,
225
+ ) -> TaskStatusUpdateEvent:
226
+ """Creates a TaskStatusUpdateEvent for running scenarios.
227
+
228
+ Args:
229
+ message: The A2A message to include.
230
+ invocation_context: The invocation context.
231
+ event: The ADK event.
232
+ task_id: Optional task ID to use for generated events.
233
+ context_id: Optional Context ID to use for generated events.
234
+
235
+
236
+ Returns:
237
+ A TaskStatusUpdateEvent with RUNNING state.
238
+ """
239
+ status = TaskStatus(
240
+ state=TaskState.working,
241
+ message=message,
242
+ timestamp=datetime.now(timezone.utc).isoformat(),
243
+ )
244
+
245
+ if any(
246
+ part.root.metadata.get(get_kagent_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY))
247
+ == A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL
248
+ and part.root.metadata.get(get_kagent_metadata_key(A2A_DATA_PART_METADATA_IS_LONG_RUNNING_KEY)) is True
249
+ and part.root.data.get("name") == REQUEST_EUC_FUNCTION_CALL_NAME
250
+ for part in message.parts
251
+ if part.root.metadata
252
+ ):
253
+ status.state = TaskState.auth_required
254
+ elif any(
255
+ part.root.metadata.get(get_kagent_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY))
256
+ == A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL
257
+ and part.root.metadata.get(get_kagent_metadata_key(A2A_DATA_PART_METADATA_IS_LONG_RUNNING_KEY)) is True
258
+ for part in message.parts
259
+ if part.root.metadata
260
+ ):
261
+ status.state = TaskState.input_required
262
+
263
+ return TaskStatusUpdateEvent(
264
+ task_id=task_id,
265
+ context_id=context_id,
266
+ status=status,
267
+ metadata=_get_context_metadata(event, invocation_context),
268
+ final=False,
269
+ )
270
+
271
+
272
+ def convert_event_to_a2a_events(
273
+ event: Event,
274
+ invocation_context: InvocationContext,
275
+ task_id: Optional[str] = None,
276
+ context_id: Optional[str] = None,
277
+ ) -> List[A2AEvent]:
278
+ """Converts a GenAI event to a list of A2A events.
279
+
280
+ Args:
281
+ event: The ADK event to convert.
282
+ invocation_context: The invocation context.
283
+ task_id: Optional task ID to use for generated events.
284
+ context_id: Optional Context ID to use for generated events.
285
+
286
+ Returns:
287
+ A list of A2A events representing the converted ADK event.
288
+
289
+ Raises:
290
+ ValueError: If required parameters are invalid.
291
+ """
292
+ if not event:
293
+ raise ValueError("Event cannot be None")
294
+ if not invocation_context:
295
+ raise ValueError("Invocation context cannot be None")
296
+
297
+ a2a_events = []
298
+
299
+ try:
300
+ # Handle error scenarios
301
+ if event.error_code:
302
+ error_event = _create_error_status_event(event, invocation_context, task_id, context_id)
303
+ a2a_events.append(error_event)
304
+
305
+ # Handle regular message content
306
+ message = convert_event_to_a2a_message(event, invocation_context)
307
+ if message:
308
+ running_event = _create_status_update_event(message, invocation_context, event, task_id, context_id)
309
+ a2a_events.append(running_event)
310
+
311
+ except Exception as e:
312
+ logger.error("Failed to convert event to A2A events: %s", e)
313
+ raise
314
+
315
+ return a2a_events
@@ -0,0 +1,206 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ module containing utilities for conversion betwen A2A Part and Google GenAI Part
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import base64
22
+ import json
23
+ import logging
24
+ from typing import Optional
25
+
26
+ from a2a import types as a2a_types
27
+ from google.genai import types as genai_types
28
+
29
+ from kagent.core.a2a import (
30
+ A2A_DATA_PART_METADATA_TYPE_CODE_EXECUTION_RESULT,
31
+ A2A_DATA_PART_METADATA_TYPE_EXECUTABLE_CODE,
32
+ A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL,
33
+ A2A_DATA_PART_METADATA_TYPE_FUNCTION_RESPONSE,
34
+ A2A_DATA_PART_METADATA_TYPE_KEY,
35
+ get_kagent_metadata_key,
36
+ )
37
+
38
+ logger = logging.getLogger("kagent_adk." + __name__)
39
+
40
+
41
+ def convert_a2a_part_to_genai_part(
42
+ a2a_part: a2a_types.Part,
43
+ ) -> Optional[genai_types.Part]:
44
+ """Convert an A2A Part to a Google GenAI Part."""
45
+ part = a2a_part.root
46
+ if isinstance(part, a2a_types.TextPart):
47
+ return genai_types.Part(text=part.text)
48
+
49
+ if isinstance(part, a2a_types.FilePart):
50
+ if isinstance(part.file, a2a_types.FileWithUri):
51
+ return genai_types.Part(
52
+ file_data=genai_types.FileData(file_uri=part.file.uri, mime_type=part.file.mime_type)
53
+ )
54
+
55
+ elif isinstance(part.file, a2a_types.FileWithBytes):
56
+ return genai_types.Part(
57
+ inline_data=genai_types.Blob(
58
+ data=base64.b64decode(part.file.bytes),
59
+ mime_type=part.file.mime_type,
60
+ )
61
+ )
62
+ else:
63
+ logger.warning(
64
+ "Cannot convert unsupported file type: %s for A2A part: %s",
65
+ type(part.file),
66
+ a2a_part,
67
+ )
68
+ return None
69
+
70
+ if isinstance(part, a2a_types.DataPart):
71
+ # Conver the Data Part to funcall and function reponse.
72
+ # This is mainly for converting human in the loop and auth request and
73
+ # response.
74
+ # TODO once A2A defined how to suervice such information, migrate below
75
+ # logic accordinlgy
76
+ if part.metadata and get_kagent_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY) in part.metadata:
77
+ if (
78
+ part.metadata[get_kagent_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY)]
79
+ == A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL
80
+ ):
81
+ return genai_types.Part(function_call=genai_types.FunctionCall.model_validate(part.data, by_alias=True))
82
+ if (
83
+ part.metadata[get_kagent_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY)]
84
+ == A2A_DATA_PART_METADATA_TYPE_FUNCTION_RESPONSE
85
+ ):
86
+ return genai_types.Part(
87
+ function_response=genai_types.FunctionResponse.model_validate(part.data, by_alias=True)
88
+ )
89
+ if (
90
+ part.metadata[get_kagent_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY)]
91
+ == A2A_DATA_PART_METADATA_TYPE_CODE_EXECUTION_RESULT
92
+ ):
93
+ return genai_types.Part(
94
+ code_execution_result=genai_types.CodeExecutionResult.model_validate(part.data, by_alias=True)
95
+ )
96
+ if (
97
+ part.metadata[get_kagent_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY)]
98
+ == A2A_DATA_PART_METADATA_TYPE_EXECUTABLE_CODE
99
+ ):
100
+ return genai_types.Part(
101
+ executable_code=genai_types.ExecutableCode.model_validate(part.data, by_alias=True)
102
+ )
103
+ return genai_types.Part(text=json.dumps(part.data))
104
+
105
+ logger.warning(
106
+ "Cannot convert unsupported part type: %s for A2A part: %s",
107
+ type(part),
108
+ a2a_part,
109
+ )
110
+ return None
111
+
112
+
113
+ def convert_genai_part_to_a2a_part(
114
+ part: genai_types.Part,
115
+ ) -> Optional[a2a_types.Part]:
116
+ """Convert a Google GenAI Part to an A2A Part."""
117
+
118
+ if part.text:
119
+ a2a_part = a2a_types.TextPart(text=part.text)
120
+ if part.thought is not None:
121
+ a2a_part.metadata = {get_kagent_metadata_key("thought"): part.thought}
122
+ return a2a_types.Part(root=a2a_part)
123
+
124
+ if part.file_data:
125
+ return a2a_types.Part(
126
+ root=a2a_types.FilePart(
127
+ file=a2a_types.FileWithUri(
128
+ uri=part.file_data.file_uri,
129
+ mime_type=part.file_data.mime_type,
130
+ )
131
+ )
132
+ )
133
+
134
+ if part.inline_data:
135
+ a2a_part = a2a_types.FilePart(
136
+ file=a2a_types.FileWithBytes(
137
+ bytes=base64.b64encode(part.inline_data.data).decode("utf-8"),
138
+ mime_type=part.inline_data.mime_type,
139
+ )
140
+ )
141
+
142
+ if part.video_metadata:
143
+ a2a_part.metadata = {
144
+ get_kagent_metadata_key("video_metadata"): part.video_metadata.model_dump(
145
+ by_alias=True, exclude_none=True
146
+ )
147
+ }
148
+
149
+ return a2a_types.Part(root=a2a_part)
150
+
151
+ # Conver the funcall and function reponse to A2A DataPart.
152
+ # This is mainly for converting human in the loop and auth request and
153
+ # response.
154
+ # TODO once A2A defined how to suervice such information, migrate below
155
+ # logic accordinlgy
156
+ if part.function_call:
157
+ return a2a_types.Part(
158
+ root=a2a_types.DataPart(
159
+ data=part.function_call.model_dump(by_alias=True, exclude_none=True),
160
+ metadata={
161
+ get_kagent_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY): A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL
162
+ },
163
+ )
164
+ )
165
+
166
+ if part.function_response:
167
+ return a2a_types.Part(
168
+ root=a2a_types.DataPart(
169
+ data=part.function_response.model_dump(by_alias=True, exclude_none=True),
170
+ metadata={
171
+ get_kagent_metadata_key(
172
+ A2A_DATA_PART_METADATA_TYPE_KEY
173
+ ): A2A_DATA_PART_METADATA_TYPE_FUNCTION_RESPONSE
174
+ },
175
+ )
176
+ )
177
+
178
+ if part.code_execution_result:
179
+ return a2a_types.Part(
180
+ root=a2a_types.DataPart(
181
+ data=part.code_execution_result.model_dump(by_alias=True, exclude_none=True),
182
+ metadata={
183
+ get_kagent_metadata_key(
184
+ A2A_DATA_PART_METADATA_TYPE_KEY
185
+ ): A2A_DATA_PART_METADATA_TYPE_CODE_EXECUTION_RESULT
186
+ },
187
+ )
188
+ )
189
+
190
+ if part.executable_code:
191
+ return a2a_types.Part(
192
+ root=a2a_types.DataPart(
193
+ data=part.executable_code.model_dump(by_alias=True, exclude_none=True),
194
+ metadata={
195
+ get_kagent_metadata_key(
196
+ A2A_DATA_PART_METADATA_TYPE_KEY
197
+ ): A2A_DATA_PART_METADATA_TYPE_EXECUTABLE_CODE
198
+ },
199
+ )
200
+ )
201
+
202
+ logger.warning(
203
+ "Cannot convert unsupported part for Google GenAI part: %s",
204
+ part,
205
+ )
206
+ return None
@@ -0,0 +1,33 @@
1
+ from typing import Any
2
+
3
+ from a2a.server.agent_execution import RequestContext
4
+ from google.adk.runners import RunConfig
5
+ from google.genai import types as genai_types
6
+
7
+ from .part_converter import convert_a2a_part_to_genai_part
8
+
9
+
10
+ def _get_user_id(request: RequestContext) -> str:
11
+ # Get user from call context if available (auth is enabled on a2a server)
12
+ if request.call_context and request.call_context.user and request.call_context.user.user_name:
13
+ return request.call_context.user.user_name
14
+
15
+ # Get user from context id
16
+ return f"A2A_USER_{request.context_id}"
17
+
18
+
19
+ def convert_a2a_request_to_adk_run_args(
20
+ request: RequestContext,
21
+ ) -> dict[str, Any]:
22
+ if not request.message:
23
+ raise ValueError("Request message cannot be None")
24
+
25
+ return {
26
+ "user_id": _get_user_id(request),
27
+ "session_id": request.context_id,
28
+ "new_message": genai_types.Content(
29
+ role="user",
30
+ parts=[convert_a2a_part_to_genai_part(part) for part in request.message.parts],
31
+ ),
32
+ "run_config": RunConfig(),
33
+ }
@@ -4,8 +4,7 @@ from typing import Literal, Self, Union
4
4
  from google.adk.agents import Agent
5
5
  from google.adk.agents.base_agent import BaseAgent
6
6
  from google.adk.agents.llm_agent import ToolUnion
7
- from google.adk.agents.remote_a2a_agent import RemoteA2aAgent
8
- from google.adk.agents.run_config import RunConfig, StreamingMode
7
+ from google.adk.agents.remote_a2a_agent import AGENT_CARD_WELL_KNOWN_PATH, RemoteA2aAgent
9
8
  from google.adk.models.anthropic_llm import Claude as ClaudeLLM
10
9
  from google.adk.models.google_llm import Gemini as GeminiLLM
11
10
  from google.adk.models.lite_llm import LiteLlm
@@ -88,16 +87,15 @@ class AgentConfig(BaseModel):
88
87
  if self.sse_tools:
89
88
  for sse_tool in self.sse_tools: # add stdio tools
90
89
  mcp_toolsets.append(MCPToolset(connection_params=sse_tool.params, tool_filter=sse_tool.tools))
91
- remote_agents: list[BaseAgent] = []
92
90
  if self.remote_agents:
93
91
  for remote_agent in self.remote_agents: # Add remote agents as tools
94
- remote_agents.append(
95
- RemoteA2aAgent(
96
- name=remote_agent.name,
97
- agent_card=remote_agent.url,
98
- description=remote_agent.description,
99
- )
92
+ remote_agent = RemoteA2aAgent(
93
+ name=remote_agent.name,
94
+ agent_card=f"{remote_agent.url}/{AGENT_CARD_WELL_KNOWN_PATH}",
95
+ description=remote_agent.description,
100
96
  )
97
+ mcp_toolsets.append(AgentTool(agent=remote_agent, skip_summarization=True))
98
+
101
99
  if self.model.type == "openai":
102
100
  model = LiteLlm(model=f"openai/{self.model.model}", base_url=self.model.base_url)
103
101
  elif self.model.type == "anthropic":
@@ -120,5 +118,4 @@ class AgentConfig(BaseModel):
120
118
  description=self.description,
121
119
  instruction=self.instruction,
122
120
  tools=mcp_toolsets,
123
- sub_agents=remote_agents,
124
121
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kagent-adk
3
- Version: 0.6.7
3
+ Version: 0.6.8
4
4
  Summary: kagent-adk is an sdk for integrating adk agents with kagent
5
5
  Requires-Python: >=3.12.11
6
6
  Requires-Dist: a2a-sdk>=0.3.1
@@ -13,15 +13,10 @@ Requires-Dist: google-auth>=2.40.2
13
13
  Requires-Dist: google-genai>=1.21.1
14
14
  Requires-Dist: httpx>=0.25.0
15
15
  Requires-Dist: jsonref>=1.1.0
16
+ Requires-Dist: kagent-core
16
17
  Requires-Dist: litellm>=1.74.3
17
18
  Requires-Dist: mcp>=1.12.0
18
19
  Requires-Dist: openai>=1.72.0
19
- Requires-Dist: opentelemetry-api>=1.36.0
20
- Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.36.0
21
- Requires-Dist: opentelemetry-instrumentation-anthropic>=0.44.0
22
- Requires-Dist: opentelemetry-instrumentation-httpx>=0.52.0
23
- Requires-Dist: opentelemetry-instrumentation-openai>=0.44.3
24
- Requires-Dist: opentelemetry-sdk>=1.36.0
25
20
  Requires-Dist: protobuf>=6
26
21
  Requires-Dist: pydantic>=2.5.0
27
22
  Requires-Dist: typer>=0.15.0
@@ -0,0 +1,15 @@
1
+ kagent/adk/__init__.py,sha256=oTnqo17F9mqzSfxs0-b_AajVSxc17KCuwHleuxdUzH4,183
2
+ kagent/adk/_a2a.py,sha256=HvduKJlfuUIKzYuPs_U_9A-xuhcMdQutZTzdLNaGORc,4406
3
+ kagent/adk/_agent_executor.py,sha256=rxNVdrcS4L5Kwe8oF7mLUnD1KOYhe6vXK-si0O-KQ9o,10730
4
+ kagent/adk/_session_service.py,sha256=A47gsfDVp8jITzeW987AHTJLEhcU_mU3ik_SFptFGIc,5815
5
+ kagent/adk/_token.py,sha256=OL46m7U5vUTby1WWjVB7Jqzig4TWddzoAmLVLlfSdAg,2515
6
+ kagent/adk/cli.py,sha256=Sdw9FXnUmeHfgJhdRDq1zpoNMf6GM7x35ZDz_OkQgJg,2963
7
+ kagent/adk/models.py,sha256=C_h8dxh_PKEXRM3kSMg5YkIxA2kbagGPfKMbpnLTZj8,4147
8
+ kagent/adk/converters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ kagent/adk/converters/event_converter.py,sha256=gPDxJhPtoIp2CAm0VjSPTH-siuz3IR23CV25AiN_1B8,10392
10
+ kagent/adk/converters/part_converter.py,sha256=Q9Kbteit8XdL_9Tb8bAtxRxOZQei8Wabszwsl4YMe2c,7507
11
+ kagent/adk/converters/request_converter.py,sha256=iTmTmhlnyRfuYyFi4WmpTSXPz22xjjotbe750j-CvYA,1072
12
+ kagent_adk-0.6.8.dist-info/METADATA,sha256=SY_f2O3Y7EcWfjH33sdFLsSHpKladu5QKMT1PLJEgw8,943
13
+ kagent_adk-0.6.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
+ kagent_adk-0.6.8.dist-info/entry_points.txt,sha256=a1Q2Inc9L0dvXWEkwnCdf9cfXdpX5Dl2Q6DhNWNjhxw,50
15
+ kagent_adk-0.6.8.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ kagent-adk = kagent.adk.cli:app
kagent_adk/_task_store.py DELETED
@@ -1,30 +0,0 @@
1
- from typing import override
2
-
3
- import httpx
4
- from a2a.server.tasks import TaskStore
5
- from a2a.types import Task
6
-
7
-
8
- class KAgentTaskStore(TaskStore):
9
- client: httpx.AsyncClient
10
-
11
- def __init__(self, client: httpx.AsyncClient):
12
- self.client = client
13
-
14
- @override
15
- async def save(self, task: Task) -> None:
16
- response = await self.client.post("/api/tasks", json=task.model_dump())
17
- response.raise_for_status()
18
-
19
- @override
20
- async def get(self, task_id: str) -> Task | None:
21
- response = await self.client.get(f"/api/tasks/{task_id}")
22
- if response.status_code == 404:
23
- return None
24
- response.raise_for_status()
25
- return Task.model_validate(response.json())
26
-
27
- @override
28
- async def delete(self, task_id: str) -> None:
29
- response = await self.client.delete(f"/api/tasks/{task_id}")
30
- response.raise_for_status()
kagent_adk/cli.py DELETED
@@ -1,202 +0,0 @@
1
- import asyncio
2
- import json
3
- import logging
4
- import os
5
- from typing import Annotated
6
-
7
- import typer
8
- import uvicorn
9
- from a2a.types import AgentCard
10
- from google.adk.cli.utils.agent_loader import AgentLoader
11
- from opentelemetry import _logs, trace
12
- from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter
13
- from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
14
- from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor
15
- from opentelemetry.instrumentation.openai import OpenAIInstrumentor
16
- from opentelemetry.sdk._events import EventLoggerProvider
17
- from opentelemetry.sdk._logs import LoggerProvider
18
- from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
19
- from opentelemetry.sdk.resources import Resource
20
- from opentelemetry.sdk.trace import TracerProvider
21
- from opentelemetry.sdk.trace.export import BatchSpanProcessor
22
-
23
- from . import AgentConfig, KAgentApp
24
-
25
- logger = logging.getLogger(__name__)
26
-
27
- app = typer.Typer()
28
-
29
- kagent_url = os.getenv("KAGENT_URL")
30
- kagent_name = os.getenv("KAGENT_NAME")
31
- kagent_namespace = os.getenv("KAGENT_NAMESPACE")
32
-
33
-
34
- class Config:
35
- _url: str
36
- _name: str
37
- _namespace: str
38
-
39
- def __init__(self):
40
- if not kagent_url:
41
- raise ValueError("KAGENT_URL is not set")
42
- if not kagent_name:
43
- raise ValueError("KAGENT_NAME is not set")
44
- if not kagent_namespace:
45
- raise ValueError("KAGENT_NAMESPACE is not set")
46
- self._url = kagent_url
47
- self._name = kagent_name
48
- self._namespace = kagent_namespace
49
-
50
- @property
51
- def name(self):
52
- return self._name.replace("-", "_")
53
-
54
- @property
55
- def namespace(self):
56
- return self._namespace.replace("-", "_")
57
-
58
- @property
59
- def app_name(self):
60
- return self.namespace + "__NS__" + self.name
61
-
62
- @property
63
- def url(self):
64
- return self._url
65
-
66
-
67
- def configure_tracing():
68
- tracing_enabled = os.getenv("OTEL_TRACING_ENABLED", "false").lower() == "true"
69
- logging_enabled = os.getenv("OTEL_LOGGING_ENABLED", "false").lower() == "true"
70
-
71
- resource = Resource({"service.name": "kagent"})
72
-
73
- # Configure tracing if enabled
74
- if tracing_enabled:
75
- logging.info("Enabling tracing")
76
- tracer_provider = TracerProvider(resource=resource)
77
- # Check new env var first, fall back to old one for backward compatibility
78
- trace_endpoint = os.getenv("OTEL_TRACING_EXPORTER_OTLP_ENDPOINT") or os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
79
- if trace_endpoint:
80
- processor = BatchSpanProcessor(OTLPSpanExporter(endpoint=trace_endpoint))
81
- else:
82
- processor = BatchSpanProcessor(OTLPSpanExporter())
83
- tracer_provider.add_span_processor(processor)
84
- trace.set_tracer_provider(tracer_provider)
85
-
86
- # Configure logging if enabled
87
- if logging_enabled:
88
- logging.info("Enabling logging for GenAI events")
89
- logger_provider = LoggerProvider(resource=resource)
90
- log_endpoint = os.getenv("OTEL_LOGGING_EXPORTER_OTLP_ENDPOINT")
91
- logging.info(f"Log endpoint configured: {log_endpoint}")
92
-
93
- # Add OTLP exporter
94
- if log_endpoint:
95
- log_processor = BatchLogRecordProcessor(OTLPLogExporter(endpoint=log_endpoint))
96
- else:
97
- log_processor = BatchLogRecordProcessor(OTLPLogExporter())
98
- logger_provider.add_log_record_processor(log_processor)
99
-
100
- _logs.set_logger_provider(logger_provider)
101
- logging.info("Log provider configured with OTLP")
102
- # When logging is enabled, use new event-based approach (input/output as log events in Body)
103
- logging.info("OpenAI instrumentation configured with event logging capability")
104
- # Create event logger provider using the configured logger provider
105
- event_logger_provider = EventLoggerProvider(logger_provider)
106
- OpenAIInstrumentor(use_legacy_attributes=False).instrument(event_logger_provider=event_logger_provider)
107
- AnthropicInstrumentor(use_legacy_attributes=False).instrument(event_logger_provider=event_logger_provider)
108
- else:
109
- # Use legacy attributes (input/output as GenAI span attributes)
110
- logging.info("OpenAI instrumentation configured with legacy GenAI span attributes")
111
- OpenAIInstrumentor().instrument()
112
- AnthropicInstrumentor().instrument()
113
-
114
-
115
- @app.command()
116
- def static(
117
- host: str = "127.0.0.1",
118
- port: int = 8080,
119
- workers: int = 1,
120
- filepath: str = "/config",
121
- reload: Annotated[bool, typer.Option("--reload")] = False,
122
- ):
123
- configure_tracing()
124
-
125
- app_cfg = Config()
126
-
127
- with open(os.path.join(filepath, "config.json"), "r") as f:
128
- config = json.load(f)
129
- agent_config = AgentConfig.model_validate(config)
130
- with open(os.path.join(filepath, "agent-card.json"), "r") as f:
131
- agent_card = json.load(f)
132
- agent_card = AgentCard.model_validate(agent_card)
133
- root_agent = agent_config.to_agent(app_cfg.name)
134
-
135
- kagent_app = KAgentApp(root_agent, agent_card, app_cfg.url, app_cfg.app_name)
136
-
137
- uvicorn.run(
138
- kagent_app.build,
139
- host=host,
140
- port=port,
141
- workers=workers,
142
- reload=reload,
143
- )
144
-
145
-
146
- @app.command()
147
- def run(
148
- name: Annotated[str, typer.Argument(help="The name of the agent to run")],
149
- working_dir: str = ".",
150
- host: str = "127.0.0.1",
151
- port: int = 8080,
152
- workers: int = 1,
153
- ):
154
- configure_tracing()
155
- app_cfg = Config()
156
-
157
- agent_loader = AgentLoader(agents_dir=working_dir)
158
- root_agent = agent_loader.load_agent(name)
159
-
160
- with open(os.path.join(working_dir, name, "agent-card.json"), "r") as f:
161
- agent_card = json.load(f)
162
- agent_card = AgentCard.model_validate(agent_card)
163
- kagent_app = KAgentApp(root_agent, agent_card, app_cfg.url, app_cfg.app_name)
164
- uvicorn.run(
165
- kagent_app.build,
166
- host=host,
167
- port=port,
168
- workers=workers,
169
- )
170
-
171
-
172
- async def test_agent(agent_config: AgentConfig, agent_card: AgentCard, task: str):
173
- app_cfg = Config()
174
- agent = agent_config.to_agent(app_cfg.name)
175
- app = KAgentApp(agent, agent_card, app_cfg.url, app_cfg.app_name)
176
- await app.test(task)
177
-
178
-
179
- @app.command()
180
- def test(
181
- task: Annotated[str, typer.Option("--task", help="The task to test the agent with")],
182
- filepath: Annotated[str, typer.Option("--filepath", help="The path to the agent config file")],
183
- ):
184
- with open(filepath, "r") as f:
185
- content = f.read()
186
- config = json.loads(content)
187
-
188
- with open(os.path.join(filepath, "agent-card.json"), "r") as f:
189
- agent_card = json.load(f)
190
- agent_card = AgentCard.model_validate(agent_card)
191
- agent_config = AgentConfig.model_validate(config)
192
- asyncio.run(test_agent(agent_config, agent_card, task))
193
-
194
-
195
- def run_cli():
196
- logging.basicConfig(level=logging.INFO)
197
- logging.info("Starting KAgent")
198
- app()
199
-
200
-
201
- if __name__ == "__main__":
202
- run_cli()
@@ -1,12 +0,0 @@
1
- kagent_adk/__init__.py,sha256=3oB8gbSzsvVmqV8w-BGKkRlH3JPfK6o27AfOD6wAd8o,182
2
- kagent_adk/_agent_executor.py,sha256=8NDMNUveFtewamYtBQ4MkCwFIRn5xDWeuDaMsW-QGF8,10806
3
- kagent_adk/_session_service.py,sha256=A47gsfDVp8jITzeW987AHTJLEhcU_mU3ik_SFptFGIc,5815
4
- kagent_adk/_task_store.py,sha256=3ApKbFfcDZmcEnwef6bCDhBhoGY9ZYwwyP671B1DHFo,889
5
- kagent_adk/_token.py,sha256=M9TOxxC4RAWPup_MIYuLIYoklnfllJS0yV3dQOfr5ng,2486
6
- kagent_adk/a2a.py,sha256=r8GgDkeKoZJx_qZmLfoaA0zSrP7t0Hn_VqEc-m0xIc8,5845
7
- kagent_adk/cli.py,sha256=fqGRM6egX0Kb2AvZWWDCv4Ad7oJ8rh0UDOsV9BZKujQ,6858
8
- kagent_adk/models.py,sha256=BCG7FdXnizGc28HyHne-GRkUUKoc-QII4b8xcIl_4Yc,4201
9
- kagent_adk-0.6.7.dist-info/METADATA,sha256=LWyROW1gHg-SbR7va1aFGOBhSRUg1QD8wAWNWaUFCY8,1242
10
- kagent_adk-0.6.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
- kagent_adk-0.6.7.dist-info/entry_points.txt,sha256=bmqHEc9zPYkbRoK57wF3wi5ohHTMB6yHTslKfvuVpc4,50
12
- kagent_adk-0.6.7.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- kagent-adk = kagent_adk.cli:app
File without changes