persona-sdk-python 0.2.17__tar.gz → 0.2.19__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 (43) hide show
  1. {persona_sdk_python-0.2.17 → persona_sdk_python-0.2.19}/PKG-INFO +39 -24
  2. {persona_sdk_python-0.2.17 → persona_sdk_python-0.2.19}/README.md +38 -23
  3. persona_sdk_python-0.2.19/bitbucket-pipelines.yml +18 -0
  4. {persona_sdk_python-0.2.17 → persona_sdk_python-0.2.19}/persona_sdk/__init__.py +50 -3
  5. persona_sdk_python-0.2.19/persona_sdk/_serde.py +20 -0
  6. persona_sdk_python-0.2.19/persona_sdk/api/agents.py +70 -0
  7. persona_sdk_python-0.2.19/persona_sdk/api/credentials.py +40 -0
  8. persona_sdk_python-0.2.19/persona_sdk/api/features.py +64 -0
  9. persona_sdk_python-0.2.19/persona_sdk/api/human_tasks.py +55 -0
  10. persona_sdk_python-0.2.19/persona_sdk/api/knowledge_bases.py +81 -0
  11. persona_sdk_python-0.2.19/persona_sdk/api/known_models.py +49 -0
  12. persona_sdk_python-0.2.19/persona_sdk/api/missions.py +74 -0
  13. persona_sdk_python-0.2.19/persona_sdk/api/projects.py +76 -0
  14. persona_sdk_python-0.2.19/persona_sdk/api/service_prices.py +47 -0
  15. persona_sdk_python-0.2.19/persona_sdk/api/sessions.py +89 -0
  16. persona_sdk_python-0.2.19/persona_sdk/api/triggers.py +61 -0
  17. persona_sdk_python-0.2.19/persona_sdk/api/workflows.py +234 -0
  18. {persona_sdk_python-0.2.17 → persona_sdk_python-0.2.19}/persona_sdk/client.py +15 -7
  19. {persona_sdk_python-0.2.17 → persona_sdk_python-0.2.19}/persona_sdk/runners/agent_runner.py +4 -3
  20. {persona_sdk_python-0.2.17 → persona_sdk_python-0.2.19}/persona_sdk/runners/resource_retriever.py +4 -4
  21. {persona_sdk_python-0.2.17 → persona_sdk_python-0.2.19}/persona_sdk/runners/tool_invoker.py +6 -6
  22. {persona_sdk_python-0.2.17 → persona_sdk_python-0.2.19}/pyproject.toml +1 -1
  23. {persona_sdk_python-0.2.17 → persona_sdk_python-0.2.19}/uv.lock +1 -9
  24. persona_sdk_python-0.2.17/bitbucket-pipelines.yml +0 -17
  25. persona_sdk_python-0.2.17/persona_sdk/api/agents.py +0 -39
  26. persona_sdk_python-0.2.17/persona_sdk/api/credentials.py +0 -21
  27. persona_sdk_python-0.2.17/persona_sdk/api/features.py +0 -30
  28. persona_sdk_python-0.2.17/persona_sdk/api/knowledge_bases.py +0 -34
  29. persona_sdk_python-0.2.17/persona_sdk/api/known_models.py +0 -24
  30. persona_sdk_python-0.2.17/persona_sdk/api/missions.py +0 -41
  31. persona_sdk_python-0.2.17/persona_sdk/api/projects.py +0 -51
  32. persona_sdk_python-0.2.17/persona_sdk/api/service_prices.py +0 -24
  33. persona_sdk_python-0.2.17/persona_sdk/api/sessions.py +0 -59
  34. persona_sdk_python-0.2.17/persona_sdk/api/triggers.py +0 -34
  35. persona_sdk_python-0.2.17/persona_sdk/api/workflows.py +0 -34
  36. {persona_sdk_python-0.2.17 → persona_sdk_python-0.2.19}/.gitignore +0 -0
  37. {persona_sdk_python-0.2.17 → persona_sdk_python-0.2.19}/DEPLOY.md +0 -0
  38. {persona_sdk_python-0.2.17 → persona_sdk_python-0.2.19}/persona_sdk/api/__init__.py +0 -0
  39. {persona_sdk_python-0.2.17 → persona_sdk_python-0.2.19}/persona_sdk/auth/__init__.py +0 -0
  40. {persona_sdk_python-0.2.17 → persona_sdk_python-0.2.19}/persona_sdk/auth/api_key_auth.py +0 -0
  41. {persona_sdk_python-0.2.17 → persona_sdk_python-0.2.19}/persona_sdk/auth/base.py +0 -0
  42. {persona_sdk_python-0.2.17 → persona_sdk_python-0.2.19}/persona_sdk/auth/bearer_token_auth.py +0 -0
  43. {persona_sdk_python-0.2.17 → persona_sdk_python-0.2.19}/persona_sdk/runners/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: persona-sdk-python
3
- Version: 0.2.17
3
+ Version: 0.2.19
4
4
  Summary: Async Python SDK for the Persona AI platform
5
5
  Project-URL: Homepage, https://persona.applica.guru
6
6
  Project-URL: Repository, https://bitbucket.org/applicaguru/persona-sdk-python
@@ -53,9 +53,10 @@ async def main():
53
53
  auth="prs_your_project_api_key",
54
54
  )
55
55
 
56
- # List agents in your project
57
- agents = await sdk.agents.list()
58
- print(agents)
56
+ # List agents in your project -> Paginated[AgentConfiguration]
57
+ page = await sdk.agents.list()
58
+ for agent in page.items:
59
+ print(agent.id, agent.name, agent.model.model_name)
59
60
 
60
61
  await sdk.close()
61
62
 
@@ -150,22 +151,37 @@ finally:
150
151
 
151
152
  ## CRUD example
152
153
 
154
+ Every API method is typed via the pydantic models re-exported from
155
+ `persona-models` (e.g. `AgentConfiguration`, `Project`, `Mission`, ...). Write
156
+ methods accept either a model instance or a plain dict; read methods always
157
+ return the parsed model (or `Paginated[...]` for list endpoints).
158
+
153
159
  ```python
154
- # Create an agent
155
- agent = await sdk.agents.create({
156
- "name": "my-agent",
157
- "model": {"modelName": "gpt-4o"},
158
- })
160
+ from persona_sdk import AgentConfiguration, ModelConfiguration
161
+
162
+ # Create an agent from a typed model
163
+ agent = await sdk.agents.create(
164
+ AgentConfiguration(
165
+ name="my-agent",
166
+ model=ModelConfiguration(model_name="gpt-4o"),
167
+ )
168
+ )
159
169
 
160
- # Read
161
- fetched = await sdk.agents.get(agent["id"])
170
+ # Read -> AgentConfiguration
171
+ fetched = await sdk.agents.get(agent.id)
162
172
 
163
- # Update
164
- fetched["name"] = "renamed-agent"
165
- await sdk.agents.update(agent["id"], fetched)
173
+ # Update (mutate the model and send it back)
174
+ fetched.name = "renamed-agent"
175
+ await sdk.agents.update(agent.id, fetched)
166
176
 
167
177
  # Delete
168
- await sdk.agents.delete(agent["id"])
178
+ await sdk.agents.delete(agent.id)
179
+ ```
180
+
181
+ A plain dict is still accepted for backwards compatibility:
182
+
183
+ ```python
184
+ agent = await sdk.agents.create({"name": "my-agent", "model": {"modelName": "gpt-4o"}})
169
185
  ```
170
186
 
171
187
  ## AI System Instructions Generation
@@ -184,22 +200,21 @@ instructions = await sdk.agents.generate_system_instructions(
184
200
  session_id="my-session-id",
185
201
  )
186
202
 
187
- # Get the project's architect agent configuration
203
+ # Get the project's architect agent configuration -> AgentConfiguration
188
204
  architect = await sdk.agents.get_architect()
189
205
  ```
190
206
 
191
207
  ## Pagination
192
208
 
193
- List endpoints accept `keyword`, `page`, and `size`:
209
+ List endpoints return a `Paginated[<Model>]` with typed `.items`, `.total`,
210
+ `.page` and `.size`:
194
211
 
195
212
  ```python
196
- result = await sdk.agents.list(keyword="bot", page=1, size=20)
197
- # {
198
- # "items": [...],
199
- # "total": 42,
200
- # "page": 1,
201
- # "size": 20
202
- # }
213
+ page = await sdk.agents.list(keyword="bot", page=1, size=20)
214
+ # page.items -> list[AgentConfiguration]
215
+ # page.total -> 42
216
+ # page.page -> 1
217
+ # page.size -> 20
203
218
  ```
204
219
 
205
220
  ## Workflows
@@ -29,9 +29,10 @@ async def main():
29
29
  auth="prs_your_project_api_key",
30
30
  )
31
31
 
32
- # List agents in your project
33
- agents = await sdk.agents.list()
34
- print(agents)
32
+ # List agents in your project -> Paginated[AgentConfiguration]
33
+ page = await sdk.agents.list()
34
+ for agent in page.items:
35
+ print(agent.id, agent.name, agent.model.model_name)
35
36
 
36
37
  await sdk.close()
37
38
 
@@ -126,22 +127,37 @@ finally:
126
127
 
127
128
  ## CRUD example
128
129
 
130
+ Every API method is typed via the pydantic models re-exported from
131
+ `persona-models` (e.g. `AgentConfiguration`, `Project`, `Mission`, ...). Write
132
+ methods accept either a model instance or a plain dict; read methods always
133
+ return the parsed model (or `Paginated[...]` for list endpoints).
134
+
129
135
  ```python
130
- # Create an agent
131
- agent = await sdk.agents.create({
132
- "name": "my-agent",
133
- "model": {"modelName": "gpt-4o"},
134
- })
136
+ from persona_sdk import AgentConfiguration, ModelConfiguration
137
+
138
+ # Create an agent from a typed model
139
+ agent = await sdk.agents.create(
140
+ AgentConfiguration(
141
+ name="my-agent",
142
+ model=ModelConfiguration(model_name="gpt-4o"),
143
+ )
144
+ )
135
145
 
136
- # Read
137
- fetched = await sdk.agents.get(agent["id"])
146
+ # Read -> AgentConfiguration
147
+ fetched = await sdk.agents.get(agent.id)
138
148
 
139
- # Update
140
- fetched["name"] = "renamed-agent"
141
- await sdk.agents.update(agent["id"], fetched)
149
+ # Update (mutate the model and send it back)
150
+ fetched.name = "renamed-agent"
151
+ await sdk.agents.update(agent.id, fetched)
142
152
 
143
153
  # Delete
144
- await sdk.agents.delete(agent["id"])
154
+ await sdk.agents.delete(agent.id)
155
+ ```
156
+
157
+ A plain dict is still accepted for backwards compatibility:
158
+
159
+ ```python
160
+ agent = await sdk.agents.create({"name": "my-agent", "model": {"modelName": "gpt-4o"}})
145
161
  ```
146
162
 
147
163
  ## AI System Instructions Generation
@@ -160,22 +176,21 @@ instructions = await sdk.agents.generate_system_instructions(
160
176
  session_id="my-session-id",
161
177
  )
162
178
 
163
- # Get the project's architect agent configuration
179
+ # Get the project's architect agent configuration -> AgentConfiguration
164
180
  architect = await sdk.agents.get_architect()
165
181
  ```
166
182
 
167
183
  ## Pagination
168
184
 
169
- List endpoints accept `keyword`, `page`, and `size`:
185
+ List endpoints return a `Paginated[<Model>]` with typed `.items`, `.total`,
186
+ `.page` and `.size`:
170
187
 
171
188
  ```python
172
- result = await sdk.agents.list(keyword="bot", page=1, size=20)
173
- # {
174
- # "items": [...],
175
- # "total": 42,
176
- # "page": 1,
177
- # "size": 20
178
- # }
189
+ page = await sdk.agents.list(keyword="bot", page=1, size=20)
190
+ # page.items -> list[AgentConfiguration]
191
+ # page.total -> 42
192
+ # page.page -> 1
193
+ # page.size -> 20
179
194
  ```
180
195
 
181
196
  ## Workflows
@@ -0,0 +1,18 @@
1
+ image: python:3.12
2
+
3
+ pipelines:
4
+ branches:
5
+ main:
6
+ - step:
7
+ name: Publish to PyPI
8
+ caches:
9
+ - pip
10
+ script:
11
+ - apt-get update && apt-get install -y git
12
+ - pip install build twine
13
+ - export VERSION=0.2.$BITBUCKET_BUILD_NUMBER
14
+ - sed -i "s/^version = .*/version = \"$VERSION\"/" pyproject.toml
15
+ - python -m build
16
+ - twine upload dist/* -u __token__ -p $PYPI_TOKEN
17
+ - git tag -a $VERSION -m "$VERSION"
18
+ - git push origin $VERSION
@@ -11,6 +11,7 @@ from persona_sdk.api.service_prices import ServicePricesApi
11
11
  from persona_sdk.api.sessions import SessionsApi
12
12
  from persona_sdk.api.triggers import TriggersApi
13
13
  from persona_sdk.api.workflows import WorkflowsApi
14
+ from persona_sdk.api.human_tasks import HumanTasksApi
14
15
  from persona_sdk.auth import (
15
16
  ApiKeyAuthenticationProvider,
16
17
  AuthenticationProvider,
@@ -18,6 +19,27 @@ from persona_sdk.auth import (
18
19
  )
19
20
  from persona_sdk.client import PersonaClient
20
21
 
22
+ # Re-export the pydantic models returned/accepted by the typed API so users can
23
+ # build requests and inspect responses without depending on ``persona-models``
24
+ # directly. These are intentionally the same objects used internally.
25
+ from persona_models.common import Paginated
26
+ from persona_models.agents import AgentConfiguration, ModelConfiguration
27
+ from persona_models.credentials import AuthorizeRequest, Credentials
28
+ from persona_models.features import FeatureTemplate
29
+ from persona_models.knowledge_bases import (
30
+ KnowledgeBase,
31
+ KnowledgeBaseDocument,
32
+ SearchChunksRequest,
33
+ )
34
+ from persona_models.known_models import KnownModel
35
+ from persona_models.mcp import GenerationRequest
36
+ from persona_models.missions import CreateMissionRequest, Mission
37
+ from persona_models.projects import Project
38
+ from persona_models.service_prices import ServicePrice, UsageCost
39
+ from persona_models.sessions import GenerationResponse, Message, Session
40
+ from persona_models.triggers import Trigger, TriggerExecutionResult
41
+ from persona_models.workflows import Workflow
42
+
21
43
  __all__ = [
22
44
  "PersonaSdk",
23
45
  "PersonaClient",
@@ -35,6 +57,30 @@ __all__ = [
35
57
  "SessionsApi",
36
58
  "TriggersApi",
37
59
  "WorkflowsApi",
60
+ "HumanTasksApi",
61
+ # Domain models (re-exported from persona-models)
62
+ "Paginated",
63
+ "AgentConfiguration",
64
+ "ModelConfiguration",
65
+ "AuthorizeRequest",
66
+ "Credentials",
67
+ "FeatureTemplate",
68
+ "KnowledgeBase",
69
+ "KnowledgeBaseDocument",
70
+ "SearchChunksRequest",
71
+ "KnownModel",
72
+ "GenerationRequest",
73
+ "GenerationResponse",
74
+ "CreateMissionRequest",
75
+ "Mission",
76
+ "Project",
77
+ "ServicePrice",
78
+ "UsageCost",
79
+ "Message",
80
+ "Session",
81
+ "Trigger",
82
+ "TriggerExecutionResult",
83
+ "Workflow",
38
84
  ]
39
85
 
40
86
 
@@ -71,9 +117,9 @@ class PersonaSdk:
71
117
  self,
72
118
  base_url: str,
73
119
  auth: Union[str, AuthenticationProvider, None] = None,
74
- proxy: str = None,
75
- workflows_url: str = None,
76
- api_key: str = None,
120
+ proxy: str | None = None,
121
+ workflows_url: str | None = None,
122
+ api_key: str | None = None,
77
123
  ):
78
124
  # Backwards compatibility: keep the legacy `api_key` keyword
79
125
  if auth is None:
@@ -87,6 +133,7 @@ class PersonaSdk:
87
133
  )
88
134
  self.agents = AgentsApi(self.client)
89
135
  self.workflows = WorkflowsApi(self.workflows_client)
136
+ self.human_tasks = HumanTasksApi(self.workflows_client)
90
137
  self.triggers = TriggersApi(self.client)
91
138
  self.sessions = SessionsApi(self.client)
92
139
  self.credentials = CredentialsApi(self.client)
@@ -0,0 +1,20 @@
1
+ """Internal (de)serialization helpers shared by the typed API classes."""
2
+ from __future__ import annotations
3
+
4
+ from typing import Any
5
+
6
+ from pydantic import BaseModel
7
+
8
+
9
+ def dump_payload(model: BaseModel | dict[str, Any] | None) -> dict[str, Any] | None:
10
+ """Serialize a write payload for an HTTP request.
11
+
12
+ Accepts either a pydantic model instance (serialized to a camelCase dict,
13
+ dropping unset/``None`` fields so server-side defaults are preserved) or a
14
+ plain dict which is returned as-is. ``None`` passes through unchanged.
15
+ """
16
+ if model is None:
17
+ return None
18
+ if isinstance(model, BaseModel):
19
+ return model.model_dump(by_alias=True, exclude_none=True)
20
+ return dict(model)
@@ -0,0 +1,70 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from persona_models.agents import AgentConfiguration
6
+ from persona_models.common import Paginated
7
+
8
+ from persona_sdk._serde import dump_payload
9
+ from persona_sdk.client import PersonaClient
10
+
11
+
12
+ class AgentsApi:
13
+ def __init__(self, client: PersonaClient):
14
+ self._client = client
15
+
16
+ async def list(
17
+ self,
18
+ keyword: str | None = None,
19
+ page: int = 1,
20
+ size: int = 20,
21
+ ) -> Paginated[AgentConfiguration]:
22
+ """List agents in the authenticated project."""
23
+ params: dict[str, Any] = {"page": page, "size": size}
24
+ if keyword:
25
+ params["keyword"] = keyword
26
+ data = await self._client.get("/agents", params=params)
27
+ return Paginated[AgentConfiguration].model_validate(data)
28
+
29
+ async def get(self, agent_id: str) -> AgentConfiguration:
30
+ return AgentConfiguration.model_validate(
31
+ await self._client.get(f"/agents/{agent_id}")
32
+ )
33
+
34
+ async def create(
35
+ self, agent: AgentConfiguration | dict[str, Any]
36
+ ) -> AgentConfiguration:
37
+ data = await self._client.post("/agents", json=dump_payload(agent))
38
+ return AgentConfiguration.model_validate(data)
39
+
40
+ async def update(
41
+ self,
42
+ agent_id: str,
43
+ agent: AgentConfiguration | dict[str, Any],
44
+ ) -> AgentConfiguration:
45
+ data = await self._client.put(f"/agents/{agent_id}", json=dump_payload(agent))
46
+ return AgentConfiguration.model_validate(data)
47
+
48
+ async def delete(self, agent_id: str) -> AgentConfiguration:
49
+ data = await self._client.delete(f"/agents/{agent_id}")
50
+ return AgentConfiguration.model_validate(data)
51
+
52
+ async def get_voices(self, synthesizer_type: str) -> dict[str, Any]:
53
+ """List available voices for a TTS synthesizer type."""
54
+ return await self._client.get(f"/values/synthesizers/{synthesizer_type}/voices")
55
+
56
+ async def generate_system_instructions(
57
+ self, prompt: str, session_id: str | None = None
58
+ ) -> str:
59
+ """Generate system instructions using the project's AI architect agent."""
60
+ body: dict[str, Any] = {"prompt": prompt}
61
+ if session_id:
62
+ body["sessionId"] = session_id
63
+ result = await self._client.post("/agents/system-instructions", json=body)
64
+ return result.get("instructions", "") if isinstance(result, dict) else ""
65
+
66
+ async def get_architect(self) -> AgentConfiguration:
67
+ """Get or create the project's architect agent configuration."""
68
+ return AgentConfiguration.model_validate(
69
+ await self._client.get("/agents/architect")
70
+ )
@@ -0,0 +1,40 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from persona_models.credentials import AuthorizeRequest, Credentials
6
+
7
+ from persona_sdk._serde import dump_payload
8
+ from persona_sdk.client import PersonaClient
9
+
10
+
11
+ class CredentialsApi:
12
+ def __init__(self, client: PersonaClient):
13
+ self._client = client
14
+
15
+ async def list(self) -> list[Credentials]:
16
+ data = await self._client.get("/credentials")
17
+ return [Credentials.model_validate(item) for item in (data or [])]
18
+
19
+ async def list_by_provider(self, provider: str) -> list[Credentials]:
20
+ data = await self._client.get(f"/credentials/{provider}")
21
+ return [Credentials.model_validate(item) for item in (data or [])]
22
+
23
+ async def get(self, credentials_id: str) -> Credentials:
24
+ data = await self._client.get(f"/credentials/all/{credentials_id}")
25
+ return Credentials.model_validate(data)
26
+
27
+ async def authorize(
28
+ self, provider: str, request: AuthorizeRequest | dict[str, Any]
29
+ ) -> dict[str, Any]:
30
+ """Authorize credentials for a provider.
31
+
32
+ Returns the server's ``AuthorizeResponse`` (``credentials``, ``state``,
33
+ ``redirect_url``) as a plain dict.
34
+ """
35
+ return await self._client.post(
36
+ f"/credentials/{provider}/authorize", json=dump_payload(request)
37
+ )
38
+
39
+ async def delete(self, credentials_id: str) -> dict[str, Any]:
40
+ return await self._client.delete(f"/credentials/all/{credentials_id}")
@@ -0,0 +1,64 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from persona_models.common import Paginated
6
+ from persona_models.features import FeatureTemplate, McpServerConfiguration
7
+
8
+ from persona_sdk._serde import dump_payload
9
+ from persona_sdk.client import PersonaClient
10
+
11
+
12
+ class FeaturesApi:
13
+ def __init__(self, client: PersonaClient):
14
+ self._client = client
15
+
16
+ async def list(
17
+ self, keyword: str | None = None, page: int = 1, size: int = 20
18
+ ) -> Paginated[FeatureTemplate]:
19
+ params: dict[str, Any] = {"page": page, "size": size}
20
+ if keyword:
21
+ params["keyword"] = keyword
22
+ data = await self._client.get("/features/templates", params=params)
23
+ return Paginated[FeatureTemplate].model_validate(data)
24
+
25
+ async def get(self, template_id: str) -> FeatureTemplate:
26
+ data = await self._client.get(f"/features/templates/{template_id}")
27
+ return FeatureTemplate.model_validate(data)
28
+
29
+ async def create(
30
+ self, feature_template: FeatureTemplate | dict[str, Any]
31
+ ) -> FeatureTemplate:
32
+ data = await self._client.post(
33
+ "/features/templates", json=dump_payload(feature_template)
34
+ )
35
+ return FeatureTemplate.model_validate(data)
36
+
37
+ async def update(
38
+ self, template_id: str, feature_template: FeatureTemplate | dict[str, Any]
39
+ ) -> FeatureTemplate:
40
+ data = await self._client.put(
41
+ f"/features/templates/{template_id}", json=dump_payload(feature_template)
42
+ )
43
+ return FeatureTemplate.model_validate(data)
44
+
45
+ async def delete(self, template_id: str) -> FeatureTemplate:
46
+ data = await self._client.delete(f"/features/templates/{template_id}")
47
+ return FeatureTemplate.model_validate(data)
48
+
49
+ async def get_mcp_tools(
50
+ self,
51
+ mcp_config: McpServerConfiguration | dict[str, Any],
52
+ credentials_id: str | None = None,
53
+ ) -> dict[str, Any]:
54
+ """List the tools exposed by an MCP server.
55
+
56
+ Returns the server's ``McpToolsResponse`` (``tools`` and optional
57
+ ``error``) as a plain dict.
58
+ """
59
+ params = {"credentialsId": credentials_id} if credentials_id else None
60
+ return await self._client.post(
61
+ "/features/templates/mcp/available-tools",
62
+ json=dump_payload(mcp_config),
63
+ params=params,
64
+ )
@@ -0,0 +1,55 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from persona_sdk.client import PersonaClient
6
+
7
+
8
+ class HumanTasksApi:
9
+ """
10
+ Human-in-the-loop tasks. A task is created when a workflow step
11
+ suspends waiting for a human (a declared ``human`` node or an agent
12
+ escalation); responding to it resumes the run.
13
+
14
+ Lives on the workflows service (same client as ``WorkflowsApi``).
15
+ Tasks are returned as plain dicts — the contract is owned by the
16
+ workflows service and intentionally not constrained here.
17
+ """
18
+
19
+ def __init__(self, client: PersonaClient):
20
+ self._client = client
21
+
22
+ async def list(
23
+ self,
24
+ status: str | None = "pending",
25
+ assignee: str | None = None,
26
+ workflow_id: str | None = None,
27
+ execution_id: str | None = None,
28
+ page: int = 1,
29
+ size: int = 50,
30
+ ) -> dict[str, Any]:
31
+ """The project's human-task inbox (``Paginated[HumanTask]`` shape)."""
32
+ params: dict[str, Any] = {"page": page, "size": size}
33
+ if status:
34
+ params["status"] = status
35
+ if assignee:
36
+ params["assignee"] = assignee
37
+ if workflow_id:
38
+ params["workflowId"] = workflow_id
39
+ if execution_id:
40
+ params["executionId"] = execution_id
41
+ return await self._client.get("/human-tasks", params=params)
42
+
43
+ async def respond(
44
+ self,
45
+ human_task_id: str,
46
+ response: Any,
47
+ responded_by: str | None = None,
48
+ ) -> dict[str, Any]:
49
+ """Answer a pending human task and resume the suspended execution."""
50
+ body: dict[str, Any] = {"response": response}
51
+ if responded_by:
52
+ body["respondedBy"] = responded_by
53
+ return await self._client.post(
54
+ f"/human-tasks/{human_task_id}/respond", json=body
55
+ )
@@ -0,0 +1,81 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from persona_models.common import Paginated
6
+ from persona_models.knowledge_bases import (
7
+ KnowledgeBase,
8
+ KnowledgeBaseDocument,
9
+ SearchChunksRequest,
10
+ )
11
+
12
+ from persona_sdk._serde import dump_payload
13
+ from persona_sdk.client import PersonaClient
14
+
15
+
16
+ class KnowledgeBasesApi:
17
+ def __init__(self, client: PersonaClient):
18
+ self._client = client
19
+
20
+ async def list(
21
+ self, keyword: str | None = None, page: int = 1, size: int = 20
22
+ ) -> Paginated[KnowledgeBase]:
23
+ params: dict[str, Any] = {"page": page, "size": size}
24
+ if keyword:
25
+ params["keyword"] = keyword
26
+ data = await self._client.get("/knowledges", params=params)
27
+ return Paginated[KnowledgeBase].model_validate(data)
28
+
29
+ async def get(self, knowledge_base_id: str) -> KnowledgeBase:
30
+ data = await self._client.get(f"/knowledges/{knowledge_base_id}")
31
+ return KnowledgeBase.model_validate(data)
32
+
33
+ async def create(
34
+ self, knowledge_base: KnowledgeBase | dict[str, Any]
35
+ ) -> KnowledgeBase:
36
+ data = await self._client.post("/knowledges", json=dump_payload(knowledge_base))
37
+ return KnowledgeBase.model_validate(data)
38
+
39
+ async def update(
40
+ self, knowledge_base_id: str, knowledge_base: KnowledgeBase | dict[str, Any]
41
+ ) -> KnowledgeBase:
42
+ data = await self._client.put(
43
+ f"/knowledges/{knowledge_base_id}", json=dump_payload(knowledge_base)
44
+ )
45
+ return KnowledgeBase.model_validate(data)
46
+
47
+ async def delete(self, knowledge_base_id: str) -> KnowledgeBase:
48
+ data = await self._client.delete(f"/knowledges/{knowledge_base_id}")
49
+ return KnowledgeBase.model_validate(data)
50
+
51
+ async def list_documents(
52
+ self, knowledge_base_id: str, page: int = 1, size: int = 20
53
+ ) -> Paginated[KnowledgeBaseDocument]:
54
+ params: dict[str, Any] = {"page": page, "size": size}
55
+ data = await self._client.get(
56
+ f"/knowledges/{knowledge_base_id}/documents", params=params
57
+ )
58
+ return Paginated[KnowledgeBaseDocument].model_validate(data)
59
+
60
+ async def delete_document(
61
+ self, knowledge_base_id: str, document_id: str
62
+ ) -> KnowledgeBaseDocument:
63
+ data = await self._client.delete(
64
+ f"/knowledges/{knowledge_base_id}/documents/{document_id}"
65
+ )
66
+ return KnowledgeBaseDocument.model_validate(data)
67
+
68
+ async def search_chunks(
69
+ self,
70
+ knowledge_base_id: str,
71
+ request: SearchChunksRequest | dict[str, Any],
72
+ ) -> list[dict[str, Any]]:
73
+ """Search a knowledge base for matching chunks.
74
+
75
+ Each chunk has the server-side ``ChunkResult`` shape
76
+ (``id``, ``text``, ``score``, ``metadata``, ``documentId``).
77
+ """
78
+ data = await self._client.post(
79
+ f"/knowledges/{knowledge_base_id}/search", json=dump_payload(request)
80
+ )
81
+ return list(data or [])