gumloop 0.3.1__tar.gz → 0.3.3__tar.gz

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.
Files changed (82) hide show
  1. {gumloop-0.3.1 → gumloop-0.3.3}/PKG-INFO +1 -1
  2. {gumloop-0.3.1 → gumloop-0.3.3}/pyproject.toml +1 -1
  3. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/__init__.py +1 -1
  4. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/_client.py +7 -3
  5. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/_http.py +2 -2
  6. gumloop-0.3.3/src/gumloop/cli/commands/brain.py +66 -0
  7. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/cli/commands/sessions.py +93 -0
  8. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/cli/main.py +2 -0
  9. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/resources/__init__.py +4 -0
  10. gumloop-0.3.3/src/gumloop/resources/agents.py +183 -0
  11. gumloop-0.3.3/src/gumloop/resources/brain.py +47 -0
  12. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/resources/sessions.py +62 -0
  13. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/types.py +100 -0
  14. gumloop-0.3.3/tests/cli/test_brain.py +81 -0
  15. {gumloop-0.3.1 → gumloop-0.3.3}/tests/cli/test_sessions.py +27 -0
  16. {gumloop-0.3.1 → gumloop-0.3.3}/tests/sdk/test_agents.py +100 -0
  17. gumloop-0.3.3/tests/sdk/test_brain.py +75 -0
  18. {gumloop-0.3.1 → gumloop-0.3.3}/tests/sdk/test_sessions.py +43 -0
  19. {gumloop-0.3.1 → gumloop-0.3.3}/uv.lock +2 -2
  20. gumloop-0.3.1/src/gumloop/resources/agents.py +0 -101
  21. {gumloop-0.3.1 → gumloop-0.3.3}/.env.example +0 -0
  22. {gumloop-0.3.1 → gumloop-0.3.3}/.gitignore +0 -0
  23. {gumloop-0.3.1 → gumloop-0.3.3}/.pre-commit-config.yaml +0 -0
  24. {gumloop-0.3.1 → gumloop-0.3.3}/.python-version +0 -0
  25. {gumloop-0.3.1 → gumloop-0.3.3}/LICENSE +0 -0
  26. {gumloop-0.3.1 → gumloop-0.3.3}/README.md +0 -0
  27. {gumloop-0.3.1 → gumloop-0.3.3}/scripts/check +0 -0
  28. {gumloop-0.3.1 → gumloop-0.3.3}/scripts/fmt +0 -0
  29. {gumloop-0.3.1 → gumloop-0.3.3}/scripts/sdk_examples.py +0 -0
  30. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/cli/__init__.py +0 -0
  31. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/cli/commands/__init__.py +0 -0
  32. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/cli/commands/_args_input.py +0 -0
  33. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/cli/commands/_downloads.py +0 -0
  34. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/cli/commands/agents.py +0 -0
  35. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/cli/commands/artifacts.py +0 -0
  36. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/cli/commands/auth.py +0 -0
  37. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/cli/commands/chat.py +0 -0
  38. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/cli/commands/mcp.py +0 -0
  39. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/cli/commands/skills.py +0 -0
  40. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/cli/console.py +0 -0
  41. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/cli/context.py +0 -0
  42. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/cli/credentials.py +0 -0
  43. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/cli/errors.py +0 -0
  44. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/cli/oauth.py +0 -0
  45. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/errors.py +0 -0
  46. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/oauth.py +0 -0
  47. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/py.typed +0 -0
  48. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/resources/artifacts.py +0 -0
  49. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/resources/chat.py +0 -0
  50. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/resources/mcp.py +0 -0
  51. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/resources/skills.py +0 -0
  52. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/resources/teams.py +0 -0
  53. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/spec/__init__.py +0 -0
  54. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/spec/_compat.py +0 -0
  55. {gumloop-0.3.1 → gumloop-0.3.3}/src/gumloop/spec/_extensions.py +0 -0
  56. {gumloop-0.3.1 → gumloop-0.3.3}/tests/__init__.py +0 -0
  57. {gumloop-0.3.1 → gumloop-0.3.3}/tests/cli/__init__.py +0 -0
  58. {gumloop-0.3.1 → gumloop-0.3.3}/tests/cli/conftest.py +0 -0
  59. {gumloop-0.3.1 → gumloop-0.3.3}/tests/cli/test_agents.py +0 -0
  60. {gumloop-0.3.1 → gumloop-0.3.3}/tests/cli/test_args_input.py +0 -0
  61. {gumloop-0.3.1 → gumloop-0.3.3}/tests/cli/test_artifacts.py +0 -0
  62. {gumloop-0.3.1 → gumloop-0.3.3}/tests/cli/test_chat.py +0 -0
  63. {gumloop-0.3.1 → gumloop-0.3.3}/tests/cli/test_context.py +0 -0
  64. {gumloop-0.3.1 → gumloop-0.3.3}/tests/cli/test_credentials.py +0 -0
  65. {gumloop-0.3.1 → gumloop-0.3.3}/tests/cli/test_login.py +0 -0
  66. {gumloop-0.3.1 → gumloop-0.3.3}/tests/cli/test_main.py +0 -0
  67. {gumloop-0.3.1 → gumloop-0.3.3}/tests/cli/test_mcp.py +0 -0
  68. {gumloop-0.3.1 → gumloop-0.3.3}/tests/cli/test_skills.py +0 -0
  69. {gumloop-0.3.1 → gumloop-0.3.3}/tests/conftest.py +0 -0
  70. {gumloop-0.3.1 → gumloop-0.3.3}/tests/integration/conftest.py +0 -0
  71. {gumloop-0.3.1 → gumloop-0.3.3}/tests/integration/test_live.py +0 -0
  72. {gumloop-0.3.1 → gumloop-0.3.3}/tests/sdk/__init__.py +0 -0
  73. {gumloop-0.3.1 → gumloop-0.3.3}/tests/sdk/conftest.py +0 -0
  74. {gumloop-0.3.1 → gumloop-0.3.3}/tests/sdk/helpers.py +0 -0
  75. {gumloop-0.3.1 → gumloop-0.3.3}/tests/sdk/test_artifacts.py +0 -0
  76. {gumloop-0.3.1 → gumloop-0.3.3}/tests/sdk/test_chat.py +0 -0
  77. {gumloop-0.3.1 → gumloop-0.3.3}/tests/sdk/test_client.py +0 -0
  78. {gumloop-0.3.1 → gumloop-0.3.3}/tests/sdk/test_mcp.py +0 -0
  79. {gumloop-0.3.1 → gumloop-0.3.3}/tests/sdk/test_oauth.py +0 -0
  80. {gumloop-0.3.1 → gumloop-0.3.3}/tests/sdk/test_skills.py +0 -0
  81. {gumloop-0.3.1 → gumloop-0.3.3}/tests/sdk/test_teams.py +0 -0
  82. {gumloop-0.3.1 → gumloop-0.3.3}/tests/test_client.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gumloop
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: Python client for the Gumloop API
5
5
  Project-URL: Homepage, https://github.com/gumloop/gumloop-py
6
6
  Project-URL: Bug Tracker, https://github.com/gumloop/gumloop-py/issues
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "gumloop"
7
- version = "0.3.1"
7
+ version = "0.3.3"
8
8
  description = "Python client for the Gumloop API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -6,7 +6,7 @@ from gumloop.errors import AuthenticationError
6
6
  from gumloop.errors import GumloopError
7
7
  from gumloop.oauth import OAuth
8
8
 
9
- __version__ = "0.3.1"
9
+ __version__ = "0.3.3"
10
10
  __all__ = [
11
11
  "APIStatusError",
12
12
  "AsyncGumloop",
@@ -15,12 +15,14 @@ from gumloop.resources import Agents
15
15
  from gumloop.resources import Artifacts
16
16
  from gumloop.resources import AsyncAgents
17
17
  from gumloop.resources import AsyncArtifacts
18
+ from gumloop.resources import AsyncBrain
18
19
  from gumloop.resources import AsyncChat
19
20
  from gumloop.resources import AsyncMCP
20
21
  from gumloop.resources import AsyncModels
21
22
  from gumloop.resources import AsyncSessions
22
23
  from gumloop.resources import AsyncSkills
23
24
  from gumloop.resources import AsyncTeams
25
+ from gumloop.resources import Brain
24
26
  from gumloop.resources import Chat
25
27
  from gumloop.resources import Models
26
28
  from gumloop.resources import Sessions
@@ -41,9 +43,9 @@ def _derive_stream_base_url(base_url: str) -> str:
41
43
  class Gumloop:
42
44
  """Sync Gumloop SDK client. Composes an :class:`HttpClient` with the
43
45
  resource classes (``agents``, ``sessions``, ``mcp``, ``teams``,
44
- ``skills``, ``artifacts``, ``models``). The :attr:`oauth` attribute
45
- holds OAuth helpers — kept off the transport because it bootstraps the
46
- bearer token rather than consuming one."""
46
+ ``skills``, ``artifacts``, ``brain``, ``models``). The :attr:`oauth`
47
+ attribute holds OAuth helpers — kept off the transport because it
48
+ bootstraps the bearer token rather than consuming one."""
47
49
 
48
50
  def __init__(
49
51
  self,
@@ -85,6 +87,7 @@ class Gumloop:
85
87
  self.teams = Teams(self._http)
86
88
  self.skills = Skills(self._http)
87
89
  self.artifacts = Artifacts(self._http)
90
+ self.brain = Brain(self._http)
88
91
  self.oauth = OAuth(base_url=self.base_url, timeout=self.timeout)
89
92
 
90
93
  def close(self) -> None:
@@ -137,6 +140,7 @@ class AsyncGumloop:
137
140
  self.teams = AsyncTeams(self._http)
138
141
  self.skills = AsyncSkills(self._http)
139
142
  self.artifacts = AsyncArtifacts(self._http)
143
+ self.brain = AsyncBrain(self._http)
140
144
  self.oauth = OAuth(base_url=self.base_url, timeout=self.timeout)
141
145
 
142
146
  async def __aenter__(self) -> AsyncGumloop:
@@ -184,7 +184,7 @@ class HttpClient:
184
184
  yield response_model.model_validate_json(event.data)
185
185
  except ValidationError:
186
186
  # Server-side mid-stream error frames or schema-drift events
187
- # land here.
187
+ # land here.
188
188
  logger.debug("dropped non-%s SSE: %s", response_model.__name__, event.data)
189
189
  continue
190
190
 
@@ -320,7 +320,7 @@ class AsyncHttpClient:
320
320
  yield response_model.model_validate_json(event.data)
321
321
  except ValidationError:
322
322
  # Server-side mid-stream error frames or schema-drift events
323
- # land here.
323
+ # land here.
324
324
  logger.debug("dropped non-%s SSE: %s", response_model.__name__, event.data)
325
325
  continue
326
326
 
@@ -0,0 +1,66 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Annotated
4
+
5
+ import typer
6
+
7
+ from gumloop import GumloopError
8
+ from gumloop.cli.console import console
9
+ from gumloop.cli.console import print_json
10
+ from gumloop.cli.context import CliContext
11
+ from gumloop.cli.errors import exit_with_error
12
+
13
+ brain_app = typer.Typer(help="Search your Company Brain.", no_args_is_help=True, rich_markup_mode="rich")
14
+
15
+
16
+ @brain_app.command(
17
+ "search",
18
+ epilog=(
19
+ "Examples:\n"
20
+ ' gumloop brain search "onboarding process"\n'
21
+ ' gumloop brain search "pricing" --limit 5 --source notion --json'
22
+ ),
23
+ )
24
+ def search_brain(
25
+ ctx: typer.Context,
26
+ query: Annotated[str, typer.Argument(help="Search query.")],
27
+ limit: Annotated[
28
+ int | None,
29
+ typer.Option("--limit", help="Maximum number of results to return (1-50)."),
30
+ ] = None,
31
+ source: Annotated[
32
+ list[str] | None,
33
+ typer.Option("--source", help="Filter by source type (repeatable), e.g. notion, google_drive, slack."),
34
+ ] = None,
35
+ json_output: Annotated[
36
+ bool,
37
+ typer.Option("--json", help="Print the raw SDK response as JSON."),
38
+ ] = False,
39
+ ) -> None:
40
+ """Search indexed Company Brain sources."""
41
+ cli: CliContext = ctx.obj
42
+ try:
43
+ response = cli.call_with_refresh(
44
+ lambda client: client.brain.search(query, limit=limit, source_type=source or None)
45
+ )
46
+ except GumloopError as error:
47
+ exit_with_error(error, json_output=json_output)
48
+
49
+ if json_output:
50
+ print_json(response)
51
+ return
52
+
53
+ if not response.results:
54
+ console.print("No results found.")
55
+ return
56
+
57
+ console.print("SCORE", "SOURCE", "TITLE", "URL", sep="\t", soft_wrap=True)
58
+ for result in response.results:
59
+ console.print(
60
+ "" if result.score is None else f"{result.score:.3f}",
61
+ result.source or "",
62
+ result.title or "",
63
+ result.url or "",
64
+ sep="\t",
65
+ soft_wrap=True,
66
+ )
@@ -93,6 +93,99 @@ def create_session(
93
93
  console.print(f" [{m.role or ''}] {content[:200]}", markup=False, highlight=False)
94
94
 
95
95
 
96
+ @sessions_app.command(
97
+ "list",
98
+ epilog=(
99
+ "Examples:\n"
100
+ " gumloop sessions list agent_abc --state completed --limit 50 --json\n"
101
+ " gumloop sessions list agent_abc --search 'invoice' --sort oldest\n"
102
+ " gumloop sessions list agent_abc --type api --creator user_123"
103
+ ),
104
+ )
105
+ def list_sessions(
106
+ ctx: typer.Context,
107
+ agent_id: Annotated[str, typer.Argument(help="Agent id whose sessions should be listed.")],
108
+ search: Annotated[
109
+ str | None,
110
+ typer.Option("--search", help="Search session name and content."),
111
+ ] = None,
112
+ state: Annotated[
113
+ str | None,
114
+ typer.Option("--state", help="Filter by state (e.g. completed, failed, processing, idle)."),
115
+ ] = None,
116
+ type_filter: Annotated[
117
+ str | None,
118
+ typer.Option("--type", help="Filter by session type (e.g. api, chat, slack)."),
119
+ ] = None,
120
+ creator: Annotated[
121
+ str | None,
122
+ typer.Option("--creator", help="Filter by creator user id."),
123
+ ] = None,
124
+ trigger_id: Annotated[
125
+ str | None,
126
+ typer.Option("--trigger-id", help="Filter by originating trigger id."),
127
+ ] = None,
128
+ sort_order: Annotated[
129
+ str | None,
130
+ typer.Option("--sort", help="Sort order (e.g. newest, oldest, credits_desc, name_asc)."),
131
+ ] = None,
132
+ limit: Annotated[
133
+ int | None,
134
+ typer.Option("--limit", help="Maximum number of sessions to return."),
135
+ ] = None,
136
+ cursor: Annotated[
137
+ str | None,
138
+ typer.Option("--cursor", help="Pagination cursor from a previous list call."),
139
+ ] = None,
140
+ json_output: Annotated[
141
+ bool,
142
+ typer.Option("--json", help="Print the raw SDK response as JSON."),
143
+ ] = False,
144
+ ) -> None:
145
+ """List an agent's sessions with search, filters, and sort."""
146
+ cli: CliContext = ctx.obj
147
+ try:
148
+ response = cli.call_with_refresh(
149
+ lambda client: client.sessions.list(
150
+ agent_id,
151
+ search=search,
152
+ state=state,
153
+ type=type_filter,
154
+ creator_user_id=creator,
155
+ trigger_id=trigger_id,
156
+ sort_order=sort_order,
157
+ page_size=limit,
158
+ cursor=cursor,
159
+ )
160
+ )
161
+ except GumloopError as error:
162
+ exit_with_error(error, json_output=json_output)
163
+
164
+ if json_output:
165
+ print_json(response)
166
+ return
167
+
168
+ if not response.sessions:
169
+ console.print("No sessions found.")
170
+ else:
171
+ console.print("ID", "STATE", "TYPE", "NAME", "CREATED", sep="\t", soft_wrap=True)
172
+ for session in response.sessions:
173
+ console.print(
174
+ session.id,
175
+ session.state or "",
176
+ session.type or "",
177
+ session.name or "",
178
+ session.created_at or "",
179
+ sep="\t",
180
+ soft_wrap=True,
181
+ markup=False,
182
+ highlight=False,
183
+ )
184
+
185
+ if response.next_cursor:
186
+ console.print(f"\n[dim]Next cursor:[/dim] {escape_markup(response.next_cursor)}")
187
+
188
+
96
189
  @sessions_app.command("get", epilog="Example:\n gumloop sessions get session_abc --json")
97
190
  def get_session(
98
191
  ctx: typer.Context,
@@ -11,6 +11,7 @@ from gumloop.cli.commands.agents import agents_app
11
11
  from gumloop.cli.commands.artifacts import artifacts_app
12
12
  from gumloop.cli.commands.auth import login as login_command
13
13
  from gumloop.cli.commands.auth import logout as logout_command
14
+ from gumloop.cli.commands.brain import brain_app
14
15
  from gumloop.cli.commands.chat import chat_app
15
16
  from gumloop.cli.commands.mcp import mcp_app
16
17
  from gumloop.cli.commands.sessions import sessions_app
@@ -97,6 +98,7 @@ app.add_typer(sessions_app, name="sessions")
97
98
  app.add_typer(skills_app, name="skills")
98
99
  app.add_typer(artifacts_app, name="artifacts")
99
100
  app.add_typer(chat_app, name="chat")
101
+ app.add_typer(brain_app, name="brain")
100
102
 
101
103
 
102
104
  def _require_supported_platform() -> None:
@@ -6,6 +6,8 @@ from gumloop.resources.agents import AsyncModels
6
6
  from gumloop.resources.agents import Models
7
7
  from gumloop.resources.artifacts import Artifacts
8
8
  from gumloop.resources.artifacts import AsyncArtifacts
9
+ from gumloop.resources.brain import AsyncBrain
10
+ from gumloop.resources.brain import Brain
9
11
  from gumloop.resources.chat import AsyncChat
10
12
  from gumloop.resources.chat import Chat
11
13
  from gumloop.resources.mcp import MCP
@@ -23,12 +25,14 @@ __all__ = [
23
25
  "Artifacts",
24
26
  "AsyncAgents",
25
27
  "AsyncArtifacts",
28
+ "AsyncBrain",
26
29
  "AsyncChat",
27
30
  "AsyncMCP",
28
31
  "AsyncModels",
29
32
  "AsyncSessions",
30
33
  "AsyncSkills",
31
34
  "AsyncTeams",
35
+ "Brain",
32
36
  "Chat",
33
37
  "Models",
34
38
  "Sessions",
@@ -0,0 +1,183 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from typing import Any
5
+
6
+ from gumloop._http import AsyncHttpClient
7
+ from gumloop._http import HttpClient
8
+ from gumloop.types import AgentCreateRequest
9
+ from gumloop.types import AgentListResponse
10
+ from gumloop.types import AgentResponse
11
+ from gumloop.types import AgentUpdateRequest
12
+ from gumloop.types import EvaluationConfigResponse
13
+ from gumloop.types import EvaluationConfigUpdateRequest
14
+ from gumloop.types import EvaluationResultListResponse
15
+ from gumloop.types import EvaluationResultResponse
16
+ from gumloop.types import ModelListResponse
17
+
18
+
19
+ class Agents:
20
+ def __init__(self, client: HttpClient) -> None:
21
+ self._client = client
22
+
23
+ def list(
24
+ self,
25
+ *,
26
+ search: str | None = None,
27
+ team_id: str | None = None,
28
+ **kwargs: Any,
29
+ ) -> AgentListResponse:
30
+ return AgentListResponse.model_validate(
31
+ self._client.get("agents", params={"search": search, "team_id": team_id, **kwargs})
32
+ )
33
+
34
+ def create(
35
+ self,
36
+ request: AgentCreateRequest | Mapping[str, Any] | None = None,
37
+ **kwargs: Any,
38
+ ) -> AgentResponse:
39
+ return AgentResponse.model_validate(
40
+ self._client.post("agents", json=AgentCreateRequest.build(request, **kwargs))
41
+ )
42
+
43
+ def retrieve(self, agent_id: str) -> AgentResponse:
44
+ return AgentResponse.model_validate(self._client.get(f"agents/{agent_id}"))
45
+
46
+ def update(
47
+ self,
48
+ agent_id: str,
49
+ request: AgentUpdateRequest | Mapping[str, Any] | None = None,
50
+ **kwargs: Any,
51
+ ) -> AgentResponse:
52
+ return AgentResponse.model_validate(
53
+ self._client.patch(f"agents/{agent_id}", json=AgentUpdateRequest.build(request, **kwargs))
54
+ )
55
+
56
+ def get_evaluation_config(self, agent_id: str) -> EvaluationConfigResponse:
57
+ return EvaluationConfigResponse.model_validate(self._client.get(f"agents/{agent_id}/evaluation-config"))
58
+
59
+ def update_evaluation_config(
60
+ self,
61
+ agent_id: str,
62
+ request: EvaluationConfigUpdateRequest | Mapping[str, Any] | None = None,
63
+ **kwargs: Any,
64
+ ) -> EvaluationConfigResponse:
65
+ """Partially update the evaluation config. Only the fields you send are changed;
66
+ omitted fields keep their current value. A provided list (criteria/tags/
67
+ data_points) replaces that list wholesale."""
68
+ return EvaluationConfigResponse.model_validate(
69
+ self._client.patch(
70
+ f"agents/{agent_id}/evaluation-config",
71
+ json=EvaluationConfigUpdateRequest.build(request, **kwargs),
72
+ )
73
+ )
74
+
75
+ def list_evaluations(
76
+ self,
77
+ agent_id: str,
78
+ *,
79
+ grade: str | None = None,
80
+ page_size: int | None = None,
81
+ cursor: str | None = None,
82
+ **kwargs: Any,
83
+ ) -> EvaluationResultListResponse:
84
+ return EvaluationResultListResponse.model_validate(
85
+ self._client.get(
86
+ f"agents/{agent_id}/evaluations",
87
+ params={"grade": grade, "page_size": page_size, "cursor": cursor, **kwargs},
88
+ )
89
+ )
90
+
91
+ def get_evaluation(self, agent_id: str, evaluation_id: str) -> EvaluationResultResponse:
92
+ return EvaluationResultResponse.model_validate(
93
+ self._client.get(f"agents/{agent_id}/evaluations/{evaluation_id}")
94
+ )
95
+
96
+
97
+ class Models:
98
+ def __init__(self, client: HttpClient) -> None:
99
+ self._client = client
100
+
101
+ def list(self, **kwargs: Any) -> ModelListResponse:
102
+ return ModelListResponse.model_validate(self._client.get("models", params=kwargs))
103
+
104
+
105
+ class AsyncAgents:
106
+ def __init__(self, client: AsyncHttpClient) -> None:
107
+ self._client = client
108
+
109
+ async def list(
110
+ self,
111
+ *,
112
+ search: str | None = None,
113
+ team_id: str | None = None,
114
+ **kwargs: Any,
115
+ ) -> AgentListResponse:
116
+ data = await self._client.get("agents", params={"search": search, "team_id": team_id, **kwargs})
117
+ return AgentListResponse.model_validate(data)
118
+
119
+ async def create(
120
+ self,
121
+ request: AgentCreateRequest | Mapping[str, Any] | None = None,
122
+ **kwargs: Any,
123
+ ) -> AgentResponse:
124
+ data = await self._client.post("agents", json=AgentCreateRequest.build(request, **kwargs))
125
+ return AgentResponse.model_validate(data)
126
+
127
+ async def retrieve(self, agent_id: str) -> AgentResponse:
128
+ return AgentResponse.model_validate(await self._client.get(f"agents/{agent_id}"))
129
+
130
+ async def update(
131
+ self,
132
+ agent_id: str,
133
+ request: AgentUpdateRequest | Mapping[str, Any] | None = None,
134
+ **kwargs: Any,
135
+ ) -> AgentResponse:
136
+ data = await self._client.patch(f"agents/{agent_id}", json=AgentUpdateRequest.build(request, **kwargs))
137
+ return AgentResponse.model_validate(data)
138
+
139
+ async def get_evaluation_config(self, agent_id: str) -> EvaluationConfigResponse:
140
+ data = await self._client.get(f"agents/{agent_id}/evaluation-config")
141
+ return EvaluationConfigResponse.model_validate(data)
142
+
143
+ async def update_evaluation_config(
144
+ self,
145
+ agent_id: str,
146
+ request: EvaluationConfigUpdateRequest | Mapping[str, Any] | None = None,
147
+ **kwargs: Any,
148
+ ) -> EvaluationConfigResponse:
149
+ """Partially update the evaluation config. Only the fields you send are changed;
150
+ omitted fields keep their current value. A provided list (criteria/tags/
151
+ data_points) replaces that list wholesale."""
152
+ data = await self._client.patch(
153
+ f"agents/{agent_id}/evaluation-config",
154
+ json=EvaluationConfigUpdateRequest.build(request, **kwargs),
155
+ )
156
+ return EvaluationConfigResponse.model_validate(data)
157
+
158
+ async def list_evaluations(
159
+ self,
160
+ agent_id: str,
161
+ *,
162
+ grade: str | None = None,
163
+ page_size: int | None = None,
164
+ cursor: str | None = None,
165
+ **kwargs: Any,
166
+ ) -> EvaluationResultListResponse:
167
+ data = await self._client.get(
168
+ f"agents/{agent_id}/evaluations",
169
+ params={"grade": grade, "page_size": page_size, "cursor": cursor, **kwargs},
170
+ )
171
+ return EvaluationResultListResponse.model_validate(data)
172
+
173
+ async def get_evaluation(self, agent_id: str, evaluation_id: str) -> EvaluationResultResponse:
174
+ data = await self._client.get(f"agents/{agent_id}/evaluations/{evaluation_id}")
175
+ return EvaluationResultResponse.model_validate(data)
176
+
177
+
178
+ class AsyncModels:
179
+ def __init__(self, client: AsyncHttpClient) -> None:
180
+ self._client = client
181
+
182
+ async def list(self, **kwargs: Any) -> ModelListResponse:
183
+ return ModelListResponse.model_validate(await self._client.get("models", params=kwargs))
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from gumloop._http import AsyncHttpClient
6
+ from gumloop._http import HttpClient
7
+ from gumloop.types import BrainSearchRequest
8
+ from gumloop.types import BrainSearchResponse
9
+
10
+
11
+ class Brain:
12
+ def __init__(self, client: HttpClient) -> None:
13
+ self._client = client
14
+
15
+ def search(
16
+ self,
17
+ query: str,
18
+ *,
19
+ limit: int | None = None,
20
+ source_type: list[str] | None = None,
21
+ **kwargs: Any,
22
+ ) -> BrainSearchResponse:
23
+ return BrainSearchResponse.model_validate(
24
+ self._client.post(
25
+ "brain/search",
26
+ json=BrainSearchRequest.build(query=query, limit=limit, source_type=source_type, **kwargs),
27
+ )
28
+ )
29
+
30
+
31
+ class AsyncBrain:
32
+ def __init__(self, client: AsyncHttpClient) -> None:
33
+ self._client = client
34
+
35
+ async def search(
36
+ self,
37
+ query: str,
38
+ *,
39
+ limit: int | None = None,
40
+ source_type: list[str] | None = None,
41
+ **kwargs: Any,
42
+ ) -> BrainSearchResponse:
43
+ data = await self._client.post(
44
+ "brain/search",
45
+ json=BrainSearchRequest.build(query=query, limit=limit, source_type=source_type, **kwargs),
46
+ )
47
+ return BrainSearchResponse.model_validate(data)
@@ -11,6 +11,7 @@ from gumloop._http import AsyncHttpClient
11
11
  from gumloop._http import HttpClient
12
12
  from gumloop.types import SessionContinueRequest
13
13
  from gumloop.types import SessionCreateRequest
14
+ from gumloop.types import SessionListResponse
14
15
  from gumloop.types import SessionResponse
15
16
  from gumloop.types import StreamEvent
16
17
 
@@ -19,6 +20,37 @@ class Sessions:
19
20
  def __init__(self, client: HttpClient) -> None:
20
21
  self._client = client
21
22
 
23
+ def list(
24
+ self,
25
+ agent_id: str,
26
+ *,
27
+ search: str | None = None,
28
+ state: str | None = None,
29
+ type: str | None = None,
30
+ creator_user_id: str | None = None,
31
+ trigger_id: str | None = None,
32
+ sort_order: str | None = None,
33
+ page_size: int | None = None,
34
+ cursor: str | None = None,
35
+ **kwargs: Any,
36
+ ) -> SessionListResponse:
37
+ return SessionListResponse.model_validate(
38
+ self._client.get(
39
+ f"agents/{agent_id}/sessions",
40
+ params={
41
+ "search": search,
42
+ "state": state,
43
+ "type": type,
44
+ "creator_user_id": creator_user_id,
45
+ "trigger_id": trigger_id,
46
+ "sort_order": sort_order,
47
+ "page_size": page_size,
48
+ "cursor": cursor,
49
+ **kwargs,
50
+ },
51
+ )
52
+ )
53
+
22
54
  @overload
23
55
  def create(
24
56
  self,
@@ -117,6 +149,36 @@ class AsyncSessions:
117
149
  def __init__(self, client: AsyncHttpClient) -> None:
118
150
  self._client = client
119
151
 
152
+ async def list(
153
+ self,
154
+ agent_id: str,
155
+ *,
156
+ search: str | None = None,
157
+ state: str | None = None,
158
+ type: str | None = None,
159
+ creator_user_id: str | None = None,
160
+ trigger_id: str | None = None,
161
+ sort_order: str | None = None,
162
+ page_size: int | None = None,
163
+ cursor: str | None = None,
164
+ **kwargs: Any,
165
+ ) -> SessionListResponse:
166
+ data = await self._client.get(
167
+ f"agents/{agent_id}/sessions",
168
+ params={
169
+ "search": search,
170
+ "state": state,
171
+ "type": type,
172
+ "creator_user_id": creator_user_id,
173
+ "trigger_id": trigger_id,
174
+ "sort_order": sort_order,
175
+ "page_size": page_size,
176
+ "cursor": cursor,
177
+ **kwargs,
178
+ },
179
+ )
180
+ return SessionListResponse.model_validate(data)
181
+
120
182
  @overload
121
183
  async def create(
122
184
  self,