octostar-python-client 0.1.759__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.
Files changed (257) hide show
  1. octostar/__init__.py +9 -0
  2. octostar/api/__init__.py +1 -0
  3. octostar/api/apps/__init__.py +0 -0
  4. octostar/api/apps/deploy_app.py +210 -0
  5. octostar/api/apps/execute_app_job.py +188 -0
  6. octostar/api/apps/get_app_logs.py +210 -0
  7. octostar/api/apps/get_apps_url.py +188 -0
  8. octostar/api/apps/get_job_logs.py +210 -0
  9. octostar/api/apps/get_job_progress.py +162 -0
  10. octostar/api/apps/kill_job.py +160 -0
  11. octostar/api/apps/list_app_jobs.py +276 -0
  12. octostar/api/apps/list_apps.py +251 -0
  13. octostar/api/apps/set_job_progress.py +216 -0
  14. octostar/api/apps/undeploy_app.py +160 -0
  15. octostar/api/metadata/__init__.py +0 -0
  16. octostar/api/metadata/get_version.py +232 -0
  17. octostar/api/metadata/get_whoami.py +232 -0
  18. octostar/api/notifications/__init__.py +0 -0
  19. octostar/api/notifications/delete_stream.py +222 -0
  20. octostar/api/notifications/get_subscriptions.py +240 -0
  21. octostar/api/notifications/publish_notification.py +275 -0
  22. octostar/api/notifications/pull_events_from_stream.py +282 -0
  23. octostar/api/notifications/push_event_to_stream.py +265 -0
  24. octostar/api/notifications/toast.py +264 -0
  25. octostar/api/ontology/__init__.py +0 -0
  26. octostar/api/ontology/fetch_ontology_data.py +275 -0
  27. octostar/api/ontology/get_ontologies.py +237 -0
  28. octostar/api/ontology/multi_query.py +297 -0
  29. octostar/api/ontology/query.py +276 -0
  30. octostar/api/pipeline/__init__.py +1 -0
  31. octostar/api/pipeline/get_processing_status.py +185 -0
  32. octostar/api/pipeline/update_processing_status.py +164 -0
  33. octostar/api/search/__init__.py +0 -0
  34. octostar/api/search/get_annotations.py +153 -0
  35. octostar/api/workspace_data/__init__.py +0 -0
  36. octostar/api/workspace_data/delete_blob.py +212 -0
  37. octostar/api/workspace_data/delete_entities.py +326 -0
  38. octostar/api/workspace_data/download_blob.py +235 -0
  39. octostar/api/workspace_data/get_attachment.py +336 -0
  40. octostar/api/workspace_data/get_files_tree.py +397 -0
  41. octostar/api/workspace_data/upload_blob.py +235 -0
  42. octostar/api/workspace_data/upsert_entities.py +284 -0
  43. octostar/api/workspace_permissions/__init__.py +0 -0
  44. octostar/api/workspace_permissions/get_permissions.py +325 -0
  45. octostar/api/workspace_tags/__init__.py +0 -0
  46. octostar/api/workspace_tags/delete_tag_from_entities.py +141 -0
  47. octostar/api/workspace_tags/tag_entities.py +180 -0
  48. octostar/client.py +492 -0
  49. octostar/errors.py +50 -0
  50. octostar/models/__init__.py +249 -0
  51. octostar/models/acknowledgement.py +74 -0
  52. octostar/models/acknowledgement_with_data.py +82 -0
  53. octostar/models/app_status.py +239 -0
  54. octostar/models/app_status_annotations.py +66 -0
  55. octostar/models/app_status_labels.py +69 -0
  56. octostar/models/app_with_url.py +82 -0
  57. octostar/models/child_processing_status.py +118 -0
  58. octostar/models/delete_entities_response_401.py +74 -0
  59. octostar/models/delete_entities_response_409.py +82 -0
  60. octostar/models/delete_entities_response_500.py +82 -0
  61. octostar/models/delete_stream_response_401.py +74 -0
  62. octostar/models/delete_tag_from_entities_response_401.py +74 -0
  63. octostar/models/deploy_app_json_body.py +90 -0
  64. octostar/models/deploy_app_json_body_secrets.py +65 -0
  65. octostar/models/deploy_app_response_200.py +98 -0
  66. octostar/models/deploy_app_response_200_data.py +60 -0
  67. octostar/models/deploy_app_response_400.py +82 -0
  68. octostar/models/deploy_app_response_403.py +82 -0
  69. octostar/models/deploy_app_response_404.py +82 -0
  70. octostar/models/deploy_app_response_409.py +82 -0
  71. octostar/models/deploy_app_response_500.py +82 -0
  72. octostar/models/entity.py +80 -0
  73. octostar/models/entity_response.py +99 -0
  74. octostar/models/entity_response_s3_urls.py +93 -0
  75. octostar/models/entity_response_s3_urls_additional_property.py +105 -0
  76. octostar/models/entity_response_s3_urls_additional_property_fields.py +114 -0
  77. octostar/models/execute_app_job_json_body.py +151 -0
  78. octostar/models/execute_app_job_json_body_annotation.py +65 -0
  79. octostar/models/execute_app_job_response_401.py +74 -0
  80. octostar/models/fetch_ontology_data_response_200.py +60 -0
  81. octostar/models/fetch_ontology_data_response_401.py +74 -0
  82. octostar/models/fetch_ontology_data_response_500.py +82 -0
  83. octostar/models/get_app_logs_response_401.py +74 -0
  84. octostar/models/get_app_logs_response_404.py +74 -0
  85. octostar/models/get_app_logs_response_500.py +82 -0
  86. octostar/models/get_apps_url_json_body.py +76 -0
  87. octostar/models/get_apps_url_response_401.py +74 -0
  88. octostar/models/get_apps_url_response_500.py +82 -0
  89. octostar/models/get_attachment_response_200.py +74 -0
  90. octostar/models/get_attachment_response_401.py +74 -0
  91. octostar/models/get_files_tree_response_200.py +106 -0
  92. octostar/models/get_files_tree_response_200_status.py +8 -0
  93. octostar/models/get_files_tree_response_400.py +111 -0
  94. octostar/models/get_files_tree_response_400_data.py +60 -0
  95. octostar/models/get_files_tree_response_400_status.py +8 -0
  96. octostar/models/get_files_tree_response_401.py +74 -0
  97. octostar/models/get_files_tree_response_500.py +111 -0
  98. octostar/models/get_files_tree_response_500_data.py +60 -0
  99. octostar/models/get_files_tree_response_500_status.py +8 -0
  100. octostar/models/get_job_logs_response_401.py +74 -0
  101. octostar/models/get_job_logs_response_404.py +74 -0
  102. octostar/models/get_job_logs_response_500.py +82 -0
  103. octostar/models/get_job_progress_response_401.py +74 -0
  104. octostar/models/get_object_response_401.py +74 -0
  105. octostar/models/get_ontologies_response_401.py +74 -0
  106. octostar/models/get_ontologies_response_500.py +81 -0
  107. octostar/models/get_permissions_response_200.py +98 -0
  108. octostar/models/get_permissions_response_400.py +82 -0
  109. octostar/models/get_permissions_response_401.py +74 -0
  110. octostar/models/get_permissions_response_500.py +82 -0
  111. octostar/models/get_processing_status_response_200.py +104 -0
  112. octostar/models/get_processing_status_response_200_data.py +87 -0
  113. octostar/models/get_processing_status_response_400.py +82 -0
  114. octostar/models/get_processing_status_response_500.py +82 -0
  115. octostar/models/get_subscriptions_response_200_item.py +74 -0
  116. octostar/models/get_version_response_200.py +74 -0
  117. octostar/models/get_version_response_404.py +74 -0
  118. octostar/models/get_whoami_response_200.py +129 -0
  119. octostar/models/get_whoami_response_401.py +74 -0
  120. octostar/models/insert_entity.py +114 -0
  121. octostar/models/insert_entity_base.py +266 -0
  122. octostar/models/insert_entity_relationships_item.py +107 -0
  123. octostar/models/insert_entity_request.py +94 -0
  124. octostar/models/internal_server_error.py +82 -0
  125. octostar/models/job_execution_result.py +146 -0
  126. octostar/models/job_status.py +196 -0
  127. octostar/models/job_status_labels.py +60 -0
  128. octostar/models/job_with_url.py +82 -0
  129. octostar/models/kill_job_response_401.py +74 -0
  130. octostar/models/list_app_jobs_response_401.py +74 -0
  131. octostar/models/list_app_jobs_response_500.py +82 -0
  132. octostar/models/list_apps_response_401.py +74 -0
  133. octostar/models/list_apps_response_500.py +82 -0
  134. octostar/models/multi_query_json_body.py +100 -0
  135. octostar/models/multi_query_json_body_queries_item.py +80 -0
  136. octostar/models/multi_query_response_400.py +82 -0
  137. octostar/models/multi_query_response_401.py +74 -0
  138. octostar/models/not_found_error.py +74 -0
  139. octostar/models/octostar_event.py +96 -0
  140. octostar/models/octostar_event_octostar_payload.py +100 -0
  141. octostar/models/octostar_event_octostar_payload_level.py +11 -0
  142. octostar/models/os_notification.py +122 -0
  143. octostar/models/processing_status.py +262 -0
  144. octostar/models/processing_status_code.py +14 -0
  145. octostar/models/progress_request.py +73 -0
  146. octostar/models/publish_notification_response_401.py +74 -0
  147. octostar/models/pull_events_from_stream_response_401.py +74 -0
  148. octostar/models/push_event_to_stream_response_401.py +74 -0
  149. octostar/models/query_json_body.py +101 -0
  150. octostar/models/query_json_body_params.py +60 -0
  151. octostar/models/query_response_400.py +82 -0
  152. octostar/models/query_response_401.py +74 -0
  153. octostar/models/set_job_progress_response_401.py +74 -0
  154. octostar/models/string_to_value_label_map.py +99 -0
  155. octostar/models/string_to_value_label_map_data.py +89 -0
  156. octostar/models/string_to_value_label_map_data_additional_property.py +80 -0
  157. octostar/models/successful_get_tags.py +103 -0
  158. octostar/models/successful_insertion.py +98 -0
  159. octostar/models/tag_entities_response_401.py +74 -0
  160. octostar/models/toast_level.py +11 -0
  161. octostar/models/toast_response_401.py +74 -0
  162. octostar/models/undeploy_app_response_401.py +74 -0
  163. octostar/models/update_processing_status_response_200.py +82 -0
  164. octostar/models/update_processing_status_response_400.py +82 -0
  165. octostar/models/update_processing_status_response_500.py +82 -0
  166. octostar/models/upsert_entities_response_401.py +74 -0
  167. octostar/models/upsert_entity.py +114 -0
  168. octostar/models/upsert_entity_base.py +266 -0
  169. octostar/models/upsert_entity_relationships_item.py +107 -0
  170. octostar/py.typed +1 -0
  171. octostar/types.py +54 -0
  172. octostar/utils/__init__.py +15 -0
  173. octostar/utils/chat/__init__.py +0 -0
  174. octostar/utils/chat/chat.py +513 -0
  175. octostar/utils/chat/detokenize.py +105 -0
  176. octostar/utils/chat/get_default_model.py +50 -0
  177. octostar/utils/chat/list_models.py +91 -0
  178. octostar/utils/chat/tokenize.py +105 -0
  179. octostar/utils/commons.py +226 -0
  180. octostar/utils/exceptions.py +134 -0
  181. octostar/utils/jobs/__init__.py +0 -0
  182. octostar/utils/jobs/apps/__init__.py +0 -0
  183. octostar/utils/jobs/apps/deploy_app.py +81 -0
  184. octostar/utils/jobs/apps/execute_app_job.py +114 -0
  185. octostar/utils/jobs/apps/get_app_logs.py +113 -0
  186. octostar/utils/jobs/apps/get_app_secret.py +102 -0
  187. octostar/utils/jobs/apps/get_apps_url.py +73 -0
  188. octostar/utils/jobs/apps/list_app_jobs.py +62 -0
  189. octostar/utils/jobs/apps/list_apps.py +126 -0
  190. octostar/utils/jobs/apps/undeploy_app.py +48 -0
  191. octostar/utils/jobs/get_job_logs.py +113 -0
  192. octostar/utils/jobs/get_job_progress.py +76 -0
  193. octostar/utils/jobs/kill_job.py +47 -0
  194. octostar/utils/jobs/set_job_progress.py +67 -0
  195. octostar/utils/meta/__init__.py +0 -0
  196. octostar/utils/meta/get_version.py +30 -0
  197. octostar/utils/meta/get_whoami.py +30 -0
  198. octostar/utils/notifications/__init__.py +0 -0
  199. octostar/utils/notifications/delete_stream.py +58 -0
  200. octostar/utils/notifications/get_my_subscriptions.py +49 -0
  201. octostar/utils/notifications/publish_notification.py +73 -0
  202. octostar/utils/notifications/pull_event_from_stream.py +63 -0
  203. octostar/utils/notifications/pull_events_from_stream.py +64 -0
  204. octostar/utils/notifications/push_event_to_stream.py +109 -0
  205. octostar/utils/notifications/push_events_to_stream.py +137 -0
  206. octostar/utils/notifications/toast.py +92 -0
  207. octostar/utils/ontology/__init__.py +10 -0
  208. octostar/utils/ontology/fetch_ontology_data.py +141 -0
  209. octostar/utils/ontology/get_ontologies.py +55 -0
  210. octostar/utils/ontology/multiquery_ontology.py +287 -0
  211. octostar/utils/ontology/query_ontology.py +186 -0
  212. octostar/utils/pipeline/__init__.py +1 -0
  213. octostar/utils/pipeline/get_processing_status.py +230 -0
  214. octostar/utils/pipeline/update_processing_status.py +286 -0
  215. octostar/utils/search/__init__.py +11 -0
  216. octostar/utils/search/bulk_update.py +138 -0
  217. octostar/utils/search/count.py +117 -0
  218. octostar/utils/search/get_entity_annotations.py +304 -0
  219. octostar/utils/search/get_index_definition.py +111 -0
  220. octostar/utils/search/multi_search.py +129 -0
  221. octostar/utils/workspace/__init__.py +0 -0
  222. octostar/utils/workspace/delete_entities.py +247 -0
  223. octostar/utils/workspace/delete_entity.py +81 -0
  224. octostar/utils/workspace/delete_relationship.py +78 -0
  225. octostar/utils/workspace/delete_relationships.py +85 -0
  226. octostar/utils/workspace/delete_temporary_blob.py +85 -0
  227. octostar/utils/workspace/extract_entities.py +140 -0
  228. octostar/utils/workspace/get_filepath_from_item.py +85 -0
  229. octostar/utils/workspace/get_filepaths_from_items.py +100 -0
  230. octostar/utils/workspace/get_files_tree.py +102 -0
  231. octostar/utils/workspace/get_item_from_filepath.py +102 -0
  232. octostar/utils/workspace/get_items_from_filepaths.py +108 -0
  233. octostar/utils/workspace/linkcharts/__init__.py +0 -0
  234. octostar/utils/workspace/linkcharts/create_linkchart.py +241 -0
  235. octostar/utils/workspace/permissions/PermissionLevel.py +8 -0
  236. octostar/utils/workspace/permissions/__init__.py +1 -0
  237. octostar/utils/workspace/permissions/get_permissions.py +81 -0
  238. octostar/utils/workspace/read_attachment.py +284 -0
  239. octostar/utils/workspace/read_file.py +113 -0
  240. octostar/utils/workspace/read_temporary_blob.py +428 -0
  241. octostar/utils/workspace/saved_searches/__init__.py +0 -0
  242. octostar/utils/workspace/saved_searches/create_saved_search.py +183 -0
  243. octostar/utils/workspace/tags/__init__.py +0 -0
  244. octostar/utils/workspace/tags/delete_tag_from_entities.py +96 -0
  245. octostar/utils/workspace/tags/tag_entities.py +175 -0
  246. octostar/utils/workspace/upsert_entities.py +268 -0
  247. octostar/utils/workspace/upsert_entity.py +110 -0
  248. octostar/utils/workspace/upsert_relationship.py +128 -0
  249. octostar/utils/workspace/upsert_relationships.py +194 -0
  250. octostar/utils/workspace/write_attachment.py +263 -0
  251. octostar/utils/workspace/write_file.py +335 -0
  252. octostar/utils/workspace/write_temporary_blob.py +218 -0
  253. octostar_python_client-0.1.759.dist-info/METADATA +159 -0
  254. octostar_python_client-0.1.759.dist-info/RECORD +257 -0
  255. octostar_python_client-0.1.759.dist-info/WHEEL +5 -0
  256. octostar_python_client-0.1.759.dist-info/licenses/LICENSE +21 -0
  257. octostar_python_client-0.1.759.dist-info/top_level.txt +1 -0
@@ -0,0 +1,513 @@
1
+ import asyncio as _asyncio
2
+ import httpx
3
+ from collections.abc import AsyncIterator, Callable
4
+ from typing import Optional, List, Any, Literal, Union, Dict, TypedDict
5
+ from pydantic import BaseModel, Field, ValidationError
6
+ from datetime import datetime
7
+ from enum import Enum
8
+ import json
9
+ import logging
10
+
11
+ from ...client import Client, get_default_client
12
+ from ..exceptions import ApiConnectionError, StopAsyncIterationWithResult
13
+ from ..commons import network_retry_strategy, TimeoutAsyncGenerator
14
+
15
+ _logger = logging.getLogger(__name__)
16
+
17
+
18
+ class EntityBase(BaseModel):
19
+ class Config:
20
+ extra = "allow"
21
+
22
+ entity_type: str = "os_workspace"
23
+ entity_id: str
24
+ entity_label: Optional[str] = None
25
+
26
+
27
+ class SearchSetModel(BaseModel):
28
+ entity_id: str
29
+ entity_label: str
30
+ query: dict
31
+
32
+
33
+ class ChatMessagePriority(Enum):
34
+ CRITICAL = 0
35
+ CONTEXTUAL = 1
36
+ HISTORICAL = 2
37
+
38
+
39
+ ContextMode = Literal["limited_to_context", "context_boosted"]
40
+
41
+
42
+ class ToolName(TypedDict):
43
+ source: str
44
+ name: str
45
+
46
+
47
+ class ExecutionMode(str, Enum):
48
+ """The execution mode for a chat request."""
49
+
50
+ COMPLETION = "completion"
51
+ """Pure pass-through: no system prompt or processing, just forwards messages to the LLM."""
52
+
53
+ SIMPLE = "simple"
54
+ """Basic chat with a system prompt, but no RAG retrieval or tool usage."""
55
+
56
+ AGENTIC = "agentic"
57
+ """Full agent: formulates a plan, retrieves context via RAG, and invokes tools as needed."""
58
+
59
+
60
+ class ChatMessage(BaseModel):
61
+ """
62
+ Represents a single chat message in the conversation. Class is taken from octostar-api lib.
63
+ """
64
+
65
+ id: str = Field(..., description="The unique ID of the message.")
66
+ role: str = Field(
67
+ ...,
68
+ description="The role of the message sender. Must be 'user', 'assistant', or 'system'.",
69
+ )
70
+ content: Union[List[EntityBase], SearchSetModel, str, Dict[str, Any]] = Field(
71
+ ..., description="The content of the message."
72
+ )
73
+ type: Optional[
74
+ Literal[
75
+ "text",
76
+ "json",
77
+ "add_entities",
78
+ "remove_entities",
79
+ "add_search_set",
80
+ "remove_search_set",
81
+ ]
82
+ ] = Field(
83
+ None,
84
+ description=(
85
+ "The type of the message. Can be 'text', 'json', 'add_entities', 'remove_entities', 'add_search_set' or 'remove_search_set."
86
+ ),
87
+ )
88
+ timestamp: Optional[datetime] = Field(
89
+ None, description="The timestamp of the message in ISO 8601 format."
90
+ )
91
+
92
+
93
+ class ChatStreamEvent(TypedDict, total=False):
94
+ """A single event from the streaming chat endpoint."""
95
+
96
+ channel: str
97
+ """The event channel: 'text', 'agentic', 'context', 'final_prompt', 'heartbeat', 'error', or 'status'."""
98
+
99
+ content: Any
100
+ """The event payload. For 'text' events this is the accumulated response text so far."""
101
+
102
+
103
+ def _build_request(
104
+ prompt: str,
105
+ *,
106
+ chat_history: Optional[list[ChatMessage]],
107
+ workspaces: Optional[list[EntityBase]],
108
+ context_mode: str,
109
+ model_name: Optional[str],
110
+ execution_mode: ExecutionMode,
111
+ disabled_tools: Optional[list[ToolName]],
112
+ streaming: bool,
113
+ client: Client,
114
+ extra_body: Optional[dict] = None,
115
+ ) -> tuple:
116
+ """Build the endpoint URL, headers, and JSON body for the chat API.
117
+
118
+ Returns:
119
+ (endpoint_url, headers, body)
120
+ """
121
+ endpoint_url = f"{client.get_base_url_v1()}/api/v1/chat"
122
+ headers = {
123
+ "Content-Type": "application/json",
124
+ # ``.token`` lives on AuthenticatedClient (the runtime type); the
125
+ # param is annotated with the Client base for call-site flexibility.
126
+ "Authorization": f"Bearer {client.token}", # pyright: ignore[reportAttributeAccessIssue]
127
+ "x-ontology": client.ontology,
128
+ }
129
+ ws_list = workspaces or []
130
+ ws_dicts = [ws.model_dump() for ws in ws_list]
131
+ for ws in ws_dicts:
132
+ if "os_workspace" not in ws:
133
+ ws["os_workspace"] = ws["entity_id"]
134
+ hist = chat_history or []
135
+ hist_dicts = [ch.model_dump() for ch in hist]
136
+ hist_dicts = [
137
+ {
138
+ **m,
139
+ "timestamp": (
140
+ m.get("timestamp", "").strftime("%Y-%m-%d %H:%M:%S")
141
+ if isinstance(m.get("timestamp"), datetime)
142
+ else None
143
+ ),
144
+ }
145
+ for m in hist_dicts
146
+ ]
147
+ body = {
148
+ **(extra_body or {}),
149
+ "model_name": model_name,
150
+ "messages": hist_dicts,
151
+ "prompt": prompt,
152
+ "streaming": streaming,
153
+ "execution_mode": execution_mode,
154
+ "context_mode": context_mode,
155
+ "api_key": "",
156
+ "cohere_key": "",
157
+ "open_workspaces": ws_dicts,
158
+ "disabled_tools": disabled_tools or [],
159
+ "deployment_url": client.get_base_url_v1(),
160
+ **(
161
+ {} if "prompt_overrides" in (extra_body or {}) else {"prompt_overrides": {}}
162
+ ),
163
+ }
164
+ return endpoint_url, headers, body
165
+
166
+
167
+ def _parse_response(message, response_validation):
168
+ if response_validation == "json" or (
169
+ isinstance(response_validation, type)
170
+ and issubclass(response_validation, BaseModel)
171
+ ):
172
+ message = message.strip()
173
+ if message.startswith("```json"):
174
+ message = message[7:]
175
+ if message.startswith("```"):
176
+ message = message[3:]
177
+ if message.endswith("```"):
178
+ message = message[:-3]
179
+ try:
180
+ message = json.loads(message)
181
+ except json.JSONDecodeError as e:
182
+ raise ValueError(f"Response is not valid JSON: {e}\nContent: {message}")
183
+ if response_validation == "json":
184
+ return message
185
+ elif isinstance(response_validation, type) and issubclass(
186
+ response_validation, BaseModel
187
+ ):
188
+ try:
189
+ return response_validation.model_validate(message)
190
+ except ValidationError as e:
191
+ raise ValueError(
192
+ f"Response does not match schema {response_validation.__name__}: {e}\nContent: {message}"
193
+ )
194
+ else:
195
+ return message
196
+ else:
197
+ return message
198
+
199
+
200
+ def sync(
201
+ prompt: str,
202
+ chat_history: Optional[list[ChatMessage]] = None,
203
+ workspaces: Optional[list[EntityBase]] = None,
204
+ context_mode: Literal["limited_to_context", "context_boosted"] = "context_boosted",
205
+ model_name: Optional[str] = None,
206
+ timeout: Optional[float] = 300.0,
207
+ retries: int = 2,
208
+ disabled_tools: Optional[list[ToolName]] = None,
209
+ execution_mode: ExecutionMode = ExecutionMode.COMPLETION,
210
+ response_validation: Union[None, Literal["json"], type[BaseModel]] = None,
211
+ client: Optional[Client] = None,
212
+ **kwargs,
213
+ ) -> Union[str, dict, list, BaseModel]:
214
+ """
215
+ # Make a request for the AI chat endpoint.
216
+
217
+ ## Arguments
218
+ - `prompt`: The user prompt to send to the chatbot.
219
+ - `chat_history`: The list of messages that precede the user prompt.
220
+ - `workspaces`: The list of workspaces the chatbot is able to see.
221
+ - `context_mode`: The filtering mode for entities retrieved by the chatbot using RAG. Can be set to:
222
+ * 'context_boosted': The context provided in the chat history is boosted in the RAG results,
223
+ but results from the whole knowledge base can be retrieved;
224
+ * 'limited_to_context': Only context provided in the chat history can be retrieved and used via RAG.
225
+ - `timeout`: The timeout for the connection to the search engine. Can be set to "None" for no enforced timeout.
226
+ - `retries`: The number of connection retries. Note that each retry may cost additional tokens.
227
+ - `disabled_tools`: A list of tools that the LLM is banned from using for this invocation.
228
+ - `execution_mode`: The mode with which the LLM will answer. Defaults to
229
+ 'completion'. Only 'completion' is supported — 'simple' and 'agentic'
230
+ are deprecated and rejected by the backend with HTTP 400:
231
+ * 'completion': Pure pass-through — no system prompt or processing, just forwards messages to the LLM;
232
+ - `response_validation`: Whether the response should follow a given pattern. Possible values are:
233
+ * None: No validation on the response;
234
+ * 'json': Response is validated to be a JSON;
235
+ * type[BaseModel]: Response is validated against the given pydantic model class;
236
+ - `client`: The Client with which to connect to Octostar. If None, the default one is used.
237
+ - `**kwargs`: Additional keyword arguments are merged into the request body.
238
+ Explicit parameters above take precedence over kwargs.
239
+
240
+ ## Returns
241
+ The chatbot response, as plain text.
242
+
243
+ ## Raises
244
+ - `ApiConnectionError`: If the query was unsuccessful.
245
+ """
246
+ if not client:
247
+ client = get_default_client()
248
+ endpoint_url, headers, body = _build_request(
249
+ prompt,
250
+ chat_history=chat_history,
251
+ workspaces=workspaces,
252
+ context_mode=context_mode,
253
+ model_name=model_name,
254
+ execution_mode=execution_mode,
255
+ disabled_tools=disabled_tools,
256
+ streaming=False,
257
+ client=client,
258
+ extra_body=kwargs,
259
+ )
260
+ response = None
261
+ try:
262
+ for attempt in network_retry_strategy(retries=retries):
263
+ with attempt:
264
+ with httpx.Client() as httpx_client:
265
+ response = httpx_client.post(
266
+ endpoint_url, json=body, headers=headers, timeout=timeout
267
+ )
268
+ response.raise_for_status()
269
+ try:
270
+ chatbot_response = response.json()["response"]["content"]
271
+ except Exception as e:
272
+ raise ConnectionError(
273
+ f"Unparsable response format {response.content} with exception {e}"
274
+ )
275
+ try:
276
+ chatbot_response = _parse_response(
277
+ chatbot_response, response_validation
278
+ )
279
+ except Exception as e:
280
+ raise ConnectionError(
281
+ f"Validation failed for {chatbot_response} with exception {e}"
282
+ )
283
+ except Exception:
284
+ raise ApiConnectionError("chat", response, client)
285
+ return chatbot_response
286
+
287
+
288
+ async def asyncio(
289
+ prompt: str,
290
+ chat_history: Optional[list[ChatMessage]] = None,
291
+ workspaces: Optional[list[EntityBase]] = None,
292
+ context_mode: Literal["limited_to_context", "context_boosted"] = "context_boosted",
293
+ model_name: Optional[str] = None,
294
+ timeout: Optional[float] = 300.0,
295
+ retries: int = 2,
296
+ disabled_tools: Optional[list[ToolName]] = None,
297
+ execution_mode: ExecutionMode = ExecutionMode.COMPLETION,
298
+ response_validation: Union[None, Literal["json"], type[BaseModel]] = None,
299
+ client: Optional[Client] = None,
300
+ **kwargs,
301
+ ) -> Union[str, dict, list, BaseModel]:
302
+ """
303
+ # Make asynchronously a request for the AI chat endpoint.
304
+
305
+ ## Arguments
306
+ - `prompt`: The user prompt to send to the chatbot.
307
+ - `chat_history`: The list of messages that precede the user prompt.
308
+ - `workspaces`: The list of workspaces the chatbot is able to see.
309
+ - `context_mode`: The filtering mode for entities retrieved by the chatbot using RAG. Can be set to:
310
+ * 'context_boosted': The context provided in the chat history is boosted in the RAG results,
311
+ but results from the whole knowledge base can be retrieved;
312
+ * 'limited_to_context': Only context provided in the chat history can be retrieved and used via RAG.
313
+ - `timeout`: The timeout for the connection to the search engine. Can be set to "None" for no enforced timeout.
314
+ - `retries`: The number of connection retries. Note that each retry may cost additional tokens.
315
+ - `disabled_tools`: A list of tools that the LLM is banned from using for this invocation.
316
+ - `execution_mode`: The mode with which the LLM will answer. Defaults to
317
+ 'completion'. Only 'completion' is supported — 'simple' and 'agentic'
318
+ are deprecated and rejected by the backend with HTTP 400:
319
+ * 'completion': Pure pass-through — no system prompt or processing, just forwards messages to the LLM;
320
+ - `response_validation`: Whether the response should follow a given pattern. Possible values are:
321
+ * None: No validation on the response, response is in plain text;
322
+ * 'json': Response is validated to be a JSON, and returned as a dict;
323
+ * type[BaseModel]: Response is validated against the given pydantic model class, and returned as an instance of that class;
324
+ - `client`: The Client with which to connect to Octostar. If None, the default one is used.
325
+ - `**kwargs`: Additional keyword arguments are merged into the request body.
326
+ Explicit parameters above take precedence over kwargs.
327
+
328
+ ## Returns
329
+ The chatbot response, parsed or as plain text.
330
+
331
+ ## Raises
332
+ - `ApiConnectionError`: If the query was unsuccessful.
333
+ """
334
+ if not client:
335
+ client = get_default_client()
336
+ endpoint_url, headers, body = _build_request(
337
+ prompt,
338
+ chat_history=chat_history,
339
+ workspaces=workspaces,
340
+ context_mode=context_mode,
341
+ model_name=model_name,
342
+ execution_mode=execution_mode,
343
+ disabled_tools=disabled_tools,
344
+ streaming=False,
345
+ client=client,
346
+ extra_body=kwargs,
347
+ )
348
+ response = None
349
+ try:
350
+ for attempt in network_retry_strategy(retries=retries):
351
+ with attempt:
352
+ async with httpx.AsyncClient() as httpx_client:
353
+ response = await httpx_client.post(
354
+ endpoint_url, json=body, headers=headers, timeout=timeout
355
+ )
356
+ response.raise_for_status()
357
+ try:
358
+ chatbot_response = response.json()["response"]["content"]
359
+ except Exception as e:
360
+ raise ConnectionError(
361
+ f"Unparsable response format {response.content} with exception {e}"
362
+ )
363
+ try:
364
+ chatbot_response = _parse_response(
365
+ chatbot_response, response_validation
366
+ )
367
+ except Exception as e:
368
+ raise ConnectionError(
369
+ f"Validation failed for {chatbot_response} with exception {e}"
370
+ )
371
+ except Exception:
372
+ raise ApiConnectionError("chat", response, client)
373
+ return chatbot_response
374
+
375
+
376
+ async def streaming(
377
+ prompt: str,
378
+ chat_history: Optional[list[ChatMessage]] = None,
379
+ workspaces: Optional[list[EntityBase]] = None,
380
+ context_mode: Literal["limited_to_context", "context_boosted"] = "context_boosted",
381
+ model_name: Optional[str] = None,
382
+ timeout: Optional[float] = 300.0,
383
+ disabled_tools: Optional[list[ToolName]] = None,
384
+ execution_mode: ExecutionMode = ExecutionMode.COMPLETION,
385
+ fail_condition: Optional[Callable[[List[ChatStreamEvent]], bool]] = None,
386
+ end_condition: Optional[Callable[[List[ChatStreamEvent]], bool]] = None,
387
+ client: Optional[Client] = None,
388
+ **kwargs,
389
+ ) -> AsyncIterator[Union[ChatStreamEvent, str]]:
390
+ """
391
+ # Stream a request to the AI chat endpoint.
392
+
393
+ Opens a streaming HTTP connection and yields parsed events as they arrive.
394
+ Each event is a `ChatStreamEvent` dict with a `channel` key indicating the
395
+ event type and a `content` key with the payload.
396
+
397
+ Catch `StopAsyncIterationWithResult` to get the final accumulated text
398
+ response after the stream ends::
399
+
400
+ try:
401
+ async for event in chat.streaming(prompt="Hello"):
402
+ print(event) # process events in real-time
403
+ except StopAsyncIterationWithResult as r:
404
+ final_text = r.value # the full response text
405
+
406
+ ## Arguments
407
+ - `prompt`: The user prompt to send to the chatbot.
408
+ - `chat_history`: The list of messages that precede the user prompt.
409
+ - `workspaces`: The list of workspaces the chatbot is able to see.
410
+ - `context_mode`: The filtering mode for entities retrieved by the chatbot using RAG. Can be set to:
411
+ * 'context_boosted': The context provided in the chat history is boosted in the RAG results,
412
+ but results from the whole knowledge base can be retrieved;
413
+ * 'limited_to_context': Only context provided in the chat history can be retrieved and used via RAG.
414
+ - `timeout`: The timeout for the connection to the search engine. Can be set to "None" for no enforced timeout.
415
+ - `disabled_tools`: A list of tools that the LLM is banned from using for this invocation.
416
+ - `execution_mode`: The mode with which the LLM will answer. Defaults to
417
+ 'completion'. Only 'completion' is supported — 'simple' and 'agentic'
418
+ are deprecated and rejected by the backend with HTTP 400:
419
+ * 'completion': Pure pass-through — no system prompt or processing, just forwards messages to the LLM;
420
+ - `fail_condition`: Custom condition to determine when the stream should be considered
421
+ failed. Receives the list of ChatStreamEvent dicts collected so far and returns
422
+ True to trigger an ApiConnectionError. If None, defaults to failing on events
423
+ with channel ``'error'``.
424
+ - `end_condition`: Custom condition to determine when the stream should stop early.
425
+ Receives the list of ChatStreamEvent dicts collected so far and returns True to
426
+ end the stream. If None, defaults to streaming until completion (or timeout).
427
+ - `client`: The Client with which to connect to Octostar. If None, the default one is used.
428
+ - `**kwargs`: Additional keyword arguments are merged into the request body.
429
+ Explicit parameters above take precedence over kwargs.
430
+
431
+ ## Yields
432
+ `ChatStreamEvent` dicts. Common channels:
433
+ - `'text'`: Accumulated generated text so far.
434
+ - `'agentic'`: Tool-call or agent status updates.
435
+ - `'context'`: RAG context entities retrieved.
436
+ - `'final_prompt'`: The full prompt sent to the LLM.
437
+ - `'heartbeat'`: Keep-alive pings (can be ignored).
438
+ - `'error'`: Error messages from the server.
439
+
440
+ ## Raises
441
+ - `StopAsyncIterationWithResult`: When the stream ends normally. `value` holds
442
+ the final accumulated text response (``str``).
443
+ - `ApiConnectionError`: If the connection or initial response failed, or the
444
+ fail_condition returned True.
445
+ """
446
+ if fail_condition is None:
447
+ fail_condition = lambda events: any(e.get("channel") == "error" for e in events)
448
+ if end_condition is None:
449
+ end_condition = lambda _events: False
450
+
451
+ if not client:
452
+ client = get_default_client()
453
+ endpoint_url, headers, body = _build_request(
454
+ prompt,
455
+ chat_history=chat_history,
456
+ workspaces=workspaces,
457
+ context_mode=context_mode,
458
+ model_name=model_name,
459
+ execution_mode=execution_mode,
460
+ disabled_tools=disabled_tools,
461
+ streaming=True,
462
+ client=client,
463
+ extra_body=kwargs,
464
+ )
465
+
466
+ async def _parse_lines(resp: httpx.Response) -> AsyncIterator[ChatStreamEvent]:
467
+ async for line in resp.aiter_lines():
468
+ if not line.strip():
469
+ continue
470
+ try:
471
+ yield json.loads(line)
472
+ except json.JSONDecodeError:
473
+ _logger.warning("chat streaming: unparsable line: %s", line[:200])
474
+
475
+ collected_events: List[ChatStreamEvent] = []
476
+ accumulated_text = ""
477
+ try:
478
+ async with httpx.AsyncClient() as httpx_client:
479
+ async with httpx_client.stream(
480
+ "POST", endpoint_url, json=body, headers=headers, timeout=timeout
481
+ ) as response:
482
+ if response.status_code != 200:
483
+ await response.aread()
484
+ raise ConnectionError(
485
+ f"chat streaming: HTTP {response.status_code}: {response.text[:500]}"
486
+ )
487
+
488
+ event_stream = _parse_lines(response)
489
+ if timeout is not None:
490
+ event_stream = TimeoutAsyncGenerator(event_stream, timeout)
491
+
492
+ try:
493
+ async for event in event_stream:
494
+ collected_events.append(event)
495
+
496
+ if event.get("channel") == "text":
497
+ accumulated_text += event.get("content", "")
498
+
499
+ if fail_condition(collected_events):
500
+ raise ConnectionError(
501
+ "chat streaming: fail_condition triggered"
502
+ )
503
+ if end_condition(collected_events):
504
+ break
505
+
506
+ yield event
507
+ except _asyncio.TimeoutError:
508
+ _logger.warning(
509
+ "chat streaming: timed out after %s seconds", timeout
510
+ )
511
+ except (ConnectionError, httpx.HTTPStatusError) as e:
512
+ raise ApiConnectionError("chat streaming", None, client) from e
513
+ raise StopAsyncIterationWithResult(value=accumulated_text)
@@ -0,0 +1,105 @@
1
+ import httpx
2
+ from typing import Optional
3
+ from ...client import Client, get_default_client
4
+ from ..exceptions import ApiConnectionError
5
+ from typing import Optional
6
+ from ..commons import network_retry_strategy
7
+
8
+
9
+ def _prepare_detokenize_request(client, tokens, model_name):
10
+ endpoint_url = f"{client.get_base_url_v1()}/api/v2/ai/llm/detokenize"
11
+ headers = {
12
+ "Authorization": f"Bearer {client.token}",
13
+ "x-ontology": client.ontology,
14
+ }
15
+ return {
16
+ "url": endpoint_url,
17
+ "headers": headers,
18
+ "timeout": 60,
19
+ "json": {
20
+ "tokens": tokens,
21
+ "model_name": model_name,
22
+ },
23
+ }
24
+
25
+
26
+ def sync(
27
+ tokens: list[int],
28
+ model_name: str,
29
+ client: Optional[Client] = None,
30
+ ) -> str:
31
+ """
32
+ # Detokenize a list of LLM tokens into a string.
33
+
34
+ ## Arguments
35
+ - `tokens`: The input list of tokens.
36
+ - `model_name`: The model with which to detokenize the input text.
37
+ - `client`: The Client with which to connect to Octostar. If None, the default one is used.
38
+
39
+ ## Returns
40
+ The detokenized string.
41
+
42
+ ## Raises
43
+ - `ApiConnectionError`: If the query was unsuccessful.
44
+ """
45
+ if not client:
46
+ client = get_default_client()
47
+ response = None
48
+ try:
49
+ for attempt in network_retry_strategy(retries=3):
50
+ with attempt:
51
+ with httpx.Client() as httpx_client:
52
+ response = httpx_client.post(
53
+ **_prepare_detokenize_request(client, tokens, model_name)
54
+ )
55
+ response.raise_for_status()
56
+ try:
57
+ response = response.json()["text"]
58
+ except Exception as e:
59
+ raise ConnectionError(
60
+ f"Unparsable response format {response.content} with exception {e}"
61
+ )
62
+ except Exception:
63
+ raise ApiConnectionError("detokenize", response, client)
64
+ return response # pyright: ignore[reportReturnType]
65
+
66
+
67
+ async def asyncio(
68
+ tokens: list[int],
69
+ model_name: str,
70
+ client: Optional[Client] = None,
71
+ ) -> str:
72
+ """
73
+ # Detokenize a list of LLM tokens into a string.
74
+
75
+ ## Arguments
76
+ - `tokens`: The input list of tokens.
77
+ - `model_name`: The model with which to detokenize the input text.
78
+ - `client`: The Client with which to connect to Octostar. If None, the default one is used.
79
+
80
+ ## Returns
81
+ The detokenized string.
82
+
83
+ ## Raises
84
+ - `ApiConnectionError`: If the query was unsuccessful.
85
+ """
86
+ if not client:
87
+ client = get_default_client()
88
+ response = None
89
+ try:
90
+ for attempt in network_retry_strategy(retries=3):
91
+ with attempt:
92
+ async with httpx.AsyncClient() as httpx_client:
93
+ response = await httpx_client.post(
94
+ **_prepare_detokenize_request(client, tokens, model_name)
95
+ )
96
+ response.raise_for_status()
97
+ try:
98
+ response = response.json()["text"]
99
+ except Exception as e:
100
+ raise ConnectionError(
101
+ f"Unparsable response format {response.content} with exception {e}"
102
+ )
103
+ except Exception:
104
+ raise ApiConnectionError("detokenize", response, client)
105
+ return response # pyright: ignore[reportReturnType]
@@ -0,0 +1,50 @@
1
+ from typing import Optional
2
+ from ...client import Client
3
+ from . import list_models
4
+
5
+
6
+ def _get_default_model_from_list(models):
7
+ default_model = list(filter(lambda x: x.get("is_default") == True, models))
8
+ if len(default_model) != 1:
9
+ raise ValueError(f"Found {len(default_model)} default models instead of 1")
10
+ return default_model[0]
11
+
12
+
13
+ def sync(
14
+ client: Optional[Client] = None,
15
+ ) -> dict:
16
+ """
17
+ # Get the current default AI chat model.
18
+
19
+ ## Arguments
20
+ - `client`: The Client with which to connect to Octostar. If None, the default one is used.
21
+
22
+ ## Returns
23
+ The information about the default AI model.
24
+
25
+ ## Raises
26
+ - `ApiConnectionError`: If the query was unsuccessful.
27
+ - `ValueError`: If no default model is available.
28
+ """
29
+ models = list_models.sync(client=client)
30
+ return _get_default_model_from_list(models)
31
+
32
+
33
+ async def asyncio(
34
+ client: Optional[Client] = None,
35
+ ) -> dict:
36
+ """
37
+ # Get asynchronously the current default AI chat model.
38
+
39
+ ## Arguments
40
+ - `client`: The Client with which to connect to Octostar. If None, the default one is used.
41
+
42
+ ## Returns
43
+ The information about the default AI model.
44
+
45
+ ## Raises
46
+ - `ApiConnectionError`: If the query was unsuccessful.
47
+ - `ValueError`: If no default model is available.
48
+ """
49
+ models = await list_models.asyncio(client=client)
50
+ return _get_default_model_from_list(models)