Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
bbc88d9
Add Python agent run trace
xTRam1 Apr 16, 2026
ae3a111
Apply review ship-blockers to Python agent trace
xTRam1 Apr 17, 2026
3e0f0b1
Merge branch 'main' into lerdogan/python-agent-trace
xTRam1 Apr 22, 2026
615e042
Merge branch 'main' into lerdogan/python-agent-trace
xTRam1 Apr 22, 2026
108ea25
Merge remote-tracking branch 'origin/main' into lerdogan/python-agent…
xTRam1 Apr 23, 2026
662d10d
feat(sdk): add reasoning effort to the Core Agent
xTRam1 Apr 24, 2026
ba8044d
test(sdk): hoist test imports to module top
xTRam1 Apr 24, 2026
40848da
Forward nested action_trace events unmodified
xTRam1 Apr 26, 2026
7ef25b1
Remove pyodide trace tests
xTRam1 Apr 28, 2026
d113896
Merge remote-tracking branch 'origin/lerdogan/python-agent-trace' int…
xTRam1 Apr 29, 2026
cd05fb7
Remove pyodide reasoning test
xTRam1 Apr 29, 2026
66e9fb3
Address Python trace review comments
xTRam1 Apr 30, 2026
b6d61be
Merge remote-tracking branch 'origin/lerdogan/python-agent-trace' int…
xTRam1 Apr 30, 2026
68150bc
Move trace models into tracing package
xTRam1 Apr 30, 2026
a33e02a
Remove trace model re-exports
xTRam1 Apr 30, 2026
b2fe7d2
Merge remote-tracking branch 'origin/main' into lerdogan/python-agent…
xTRam1 Apr 30, 2026
8c117d9
Merge remote-tracking branch 'origin/lerdogan/python-agent-trace' int…
xTRam1 Apr 30, 2026
c6636da
Fix tracing package formatting
xTRam1 Apr 30, 2026
9821741
Merge remote-tracking branch 'origin/lerdogan/python-agent-trace' int…
xTRam1 Apr 30, 2026
f83f91c
Merge remote-tracking branch 'origin/main' into lerdogan/sdk-core-age…
xTRam1 Apr 30, 2026
8d01131
Bump package versions for reasoning support
xTRam1 Apr 30, 2026
d007cd4
Simplify reasoning effort docstring
xTRam1 Apr 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/narada-core/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "narada-core"
version = "0.0.20"
version = "0.0.21"
description = "Code shared by the `narada` and `narada-pyodide` packages."
license = "Apache-2.0"
readme = "README.md"
Expand Down
12 changes: 12 additions & 0 deletions packages/narada-core/src/narada_core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ def prompt_prefix(self) -> str:
return "/coreAgent "


class ReasoningEffort(StrEnum):
"""Controls how much reasoning the Core Agent uses before responding.

Only `Agent.CORE_AGENT` supports this option; other agents raise `ValueError`.
"""

NONE = "none"
LOW = "low"
MEDIUM = "medium"
HIGH = "high"


class UserResourceCredentials(TypedDict, total=False):
salesforce: dict[str, str]
jira: dict[str, str]
Expand Down
4 changes: 2 additions & 2 deletions packages/narada-pyodide/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@

[project]
name = "narada-pyodide"
version = "0.0.47"
version = "0.0.48"
description = "Pyodide-compatible Python client SDK for Narada"
license = "Apache-2.0"
readme = "README.md"
authors = [{ name = "Narada", email = "support@narada.ai" }]
requires-python = ">=3.12"
dependencies = [
"narada-core==0.0.20",
"narada-core==0.0.21",
# Must be a supported version in https://pyodide.org/en/stable/usage/packages-in-pyodide.html
"packaging==24.2",
]
Expand Down
3 changes: 2 additions & 1 deletion packages/narada-pyodide/src/narada/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
NaradaError,
NaradaTimeoutError,
)
from narada_core.models import Agent, File, Response, ResponseContent
from narada_core.models import Agent, File, ReasoningEffort, Response, ResponseContent

from narada.client import Narada
from narada.utils import download_file, render_html
Expand All @@ -23,6 +23,7 @@
"Narada",
"NaradaError",
"NaradaTimeoutError",
"ReasoningEffort",
"RemoteBrowserWindow",
"render_html",
"Response",
Expand Down
155 changes: 143 additions & 12 deletions packages/narada-pyodide/src/narada/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
Agent,
File,
McpServer,
ReasoningEffort,
RemoteDispatchChatHistoryItem,
Response,
UserResourceCredentials,
Expand Down Expand Up @@ -199,6 +200,57 @@ async def upload_file(self, *, file: IO) -> File:
"Uploading files is not supported in the browser environment"
)

# `reasoning` is only valid with the Core Agent; these two overloads make
# that constraint type-checkable. Generic-agent calls fall through to the
# general overloads below, which do not accept a `reasoning` argument.
@overload
async def dispatch_request(
self,
*,
prompt: str,
agent: Literal[Agent.CORE_AGENT],
reasoning: ReasoningEffort | None = None,
clear_chat: bool | None = None,
generate_gif: bool | None = None,
output_schema: None = None,
previous_request_id: str | None = None,
chat_history: list[RemoteDispatchChatHistoryItem] | None = None,
additional_context: dict[str, str] | None = None,
time_zone: str = "America/Los_Angeles",
user_resource_credentials: UserResourceCredentials | None = None,
mcp_servers: list[McpServer] | None = None,
secret_variables: dict[str, str] | None = None,
input_variables: dict[str, Any] | None = None,
callback_url: str | None = None,
callback_secret: str | None = None,
callback_headers: dict[str, Any] | None = None,
timeout: int = 1000,
) -> Response[None]: ...

@overload
async def dispatch_request(
self,
*,
prompt: str,
agent: Literal[Agent.CORE_AGENT],
reasoning: ReasoningEffort | None = None,
clear_chat: bool | None = None,
generate_gif: bool | None = None,
output_schema: type[_StructuredOutput],
previous_request_id: str | None = None,
chat_history: list[RemoteDispatchChatHistoryItem] | None = None,
additional_context: dict[str, str] | None = None,
time_zone: str = "America/Los_Angeles",
user_resource_credentials: UserResourceCredentials | None = None,
mcp_servers: list[McpServer] | None = None,
secret_variables: dict[str, str] | None = None,
input_variables: dict[str, Any] | None = None,
callback_url: str | None = None,
callback_secret: str | None = None,
callback_headers: dict[str, Any] | None = None,
timeout: int = 1000,
) -> Response[_StructuredOutput]: ...

@overload
async def dispatch_request(
self,
Expand Down Expand Up @@ -250,6 +302,7 @@ async def dispatch_request(
*,
prompt: str,
agent: Agent | str = Agent.OPERATOR,
reasoning: ReasoningEffort | None = None,
clear_chat: bool | None = None,
generate_gif: bool | None = None,
output_schema: type[BaseModel] | None = None,
Expand All @@ -270,6 +323,14 @@ async def dispatch_request(

The higher-level `agent` method should be preferred for most use cases.
"""
# The overloads enforce this at type-check time when callers use
# ``Agent.CORE_AGENT``; the runtime check covers string-form agents
# (``agent="..."``) and callers without a type checker.
if reasoning is not None and agent is not Agent.CORE_AGENT:
raise ValueError(
"`reasoning` is only supported with `agent=Agent.CORE_AGENT` "
f"(got agent={agent!r})"
Comment thread
xTRam1 marked this conversation as resolved.
)
# Trace instrumentation: the entire method body is wrapped so that any
# exit (successful return, timeout, or non-timeout failure) produces a
# ``subAgentCall`` trace event with matching status. See `_trace.py`.
Expand Down Expand Up @@ -322,6 +383,8 @@ async def dispatch_request(
body["callbackSecret"] = callback_secret
if callback_headers is not None:
body["callbackHeaders"] = callback_headers
if reasoning is not None:
body["reasoningMode"] = reasoning.value

try:
controller = AbortController.new()
Expand Down Expand Up @@ -439,6 +502,42 @@ async def dispatch_request(
)
raise

# `reasoning` is only valid with the Core Agent. See `dispatch_request`
# above for the rationale; the same overload pattern is mirrored here.
@overload
async def agent(
self,
*,
prompt: str,
agent: Literal[Agent.CORE_AGENT],
reasoning: ReasoningEffort | None = None,
clear_chat: bool | None = None,
generate_gif: bool | None = None,
output_schema: None = None,
time_zone: str = "America/Los_Angeles",
mcp_servers: list[McpServer] | None = None,
secret_variables: dict[str, str] | None = None,
input_variables: dict[str, Any] | None = None,
timeout: int = 1000,
) -> AgentResponse[dict[str, Any]]: ...

@overload
async def agent(
self,
*,
prompt: str,
agent: Literal[Agent.CORE_AGENT],
reasoning: ReasoningEffort | None = None,
clear_chat: bool | None = None,
generate_gif: bool | None = None,
output_schema: type[_StructuredOutput],
time_zone: str = "America/Los_Angeles",
mcp_servers: list[McpServer] | None = None,
secret_variables: dict[str, str] | None = None,
input_variables: dict[str, Any] | None = None,
timeout: int = 1000,
) -> AgentResponse[_StructuredOutput]: ...

@overload
async def agent(
self,
Expand Down Expand Up @@ -476,6 +575,7 @@ async def agent(
*,
prompt: str,
agent: Agent | str = Agent.OPERATOR,
reasoning: ReasoningEffort | None = None,
clear_chat: bool | None = None,
generate_gif: bool | None = None,
output_schema: type[BaseModel] | None = None,
Expand All @@ -486,18 +586,49 @@ async def agent(
timeout: int = 1000,
) -> AgentResponse:
"""Invokes an agent in the Narada extension side panel chat."""
remote_dispatch_response = await self.dispatch_request(
prompt=prompt,
agent=agent,
clear_chat=clear_chat,
generate_gif=generate_gif,
output_schema=output_schema,
time_zone=time_zone,
mcp_servers=mcp_servers,
secret_variables=secret_variables,
input_variables=input_variables,
timeout=timeout,
)
# Branch on `reasoning` so each call site binds a single, typed overload
# of `dispatch_request`. The validation also lives in `dispatch_request`
# itself (defense in depth + reachable when callers go straight to the
# low-level API), so the redundancy here is intentional.
if reasoning is None:
remote_dispatch_response = await self.dispatch_request(
prompt=prompt,
agent=agent,
clear_chat=clear_chat,
generate_gif=generate_gif,
output_schema=output_schema,
time_zone=time_zone,
mcp_servers=mcp_servers,
secret_variables=secret_variables,
input_variables=input_variables,
timeout=timeout,
)
else:
if agent is not Agent.CORE_AGENT:
raise ValueError(
"`reasoning` is only supported with `agent=Agent.CORE_AGENT` "
f"(got agent={agent!r})"
)
# The CORE_AGENT-specific overloads of `dispatch_request` split on
# a narrower `output_schema` discriminator (None vs `type[T]`),
# which the impl's `type[BaseModel] | None` union doesn't cleanly
# narrow into without further branching. The public `agent()`
# overloads above already give callers correct return-type
# narrowing, so the internal forward call bypasses overload
# disambiguation on this single dimension.
remote_dispatch_response = await self.dispatch_request( # pyright: ignore[reportCallIssue]
prompt=prompt,
agent=agent,
reasoning=reasoning,
clear_chat=clear_chat,
generate_gif=generate_gif,
output_schema=output_schema, # pyright: ignore[reportArgumentType]
time_zone=time_zone,
mcp_servers=mcp_servers,
secret_variables=secret_variables,
input_variables=input_variables,
timeout=timeout,
)
response_content = remote_dispatch_response["response"]
assert response_content is not None

Expand Down
4 changes: 2 additions & 2 deletions packages/narada/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[project]
name = "narada"
version = "0.1.47"
version = "0.1.48"
description = "Python client SDK for Narada"
license = "Apache-2.0"
readme = "README.md"
authors = [{ name = "Narada", email = "support@narada.ai" }]
requires-python = ">=3.12"
dependencies = [
"narada-core==0.0.20",
"narada-core==0.0.21",
"aiohttp>=3.12.13",
"playwright>=1.53.0",
"rich>=14.0.0",
Expand Down
3 changes: 2 additions & 1 deletion packages/narada/src/narada/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
NaradaUnsupportedBrowserError,
UserAbortedError,
)
from narada_core.models import Agent, File, Response, ResponseContent
from narada_core.models import Agent, File, ReasoningEffort, Response, ResponseContent

from narada.client import Narada
from narada.config import BrowserConfig, ProxyConfig
Expand All @@ -31,6 +31,7 @@
"NaradaTimeoutError",
"NaradaUnsupportedBrowserError",
"ProxyConfig",
"ReasoningEffort",
"RemoteBrowserWindow",
"render_html",
"Response",
Expand Down
Loading
Loading