kotharcomputing 0.76.0__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.
@@ -0,0 +1,16 @@
1
+ from .client import (
2
+ KotharClient,
3
+ KotharClientOptions,
4
+ RawResponse,
5
+ )
6
+ from .executions import ExecuteResult
7
+ from .errors import KOTHAR_ERROR_TYPE_BASE_URL, KotharApiError
8
+
9
+ __all__ = [
10
+ "KOTHAR_ERROR_TYPE_BASE_URL",
11
+ "KotharApiError",
12
+ "KotharClient",
13
+ "KotharClientOptions",
14
+ "RawResponse",
15
+ "ExecuteResult",
16
+ ]
@@ -0,0 +1,186 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Literal, NotRequired, TypeAlias, TypedDict, cast
4
+
5
+ from .fetch import ApiTransport
6
+ from .users import WithUser
7
+
8
+
9
+ class ActivityExecution(TypedDict):
10
+ type: Literal["execution"]
11
+ id: str
12
+
13
+
14
+ class ActivityJob(TypedDict):
15
+ type: Literal["job"]
16
+ id: str
17
+
18
+
19
+ Activity: TypeAlias = ActivityExecution | ActivityJob
20
+
21
+ AgentKind: TypeAlias = Literal["personal", "cloud"]
22
+ AgentStatus: TypeAlias = Literal["offline", "online"]
23
+ AgentCapabilities: TypeAlias = Literal["direct-execution", "job"]
24
+
25
+
26
+ class AgentSystemInfoCPU(TypedDict):
27
+ arch: str
28
+ parallelism: int
29
+ flags: list[str]
30
+ vendor: NotRequired[str]
31
+ model: NotRequired[str]
32
+
33
+
34
+ class AgentSystemInfoMemory(TypedDict):
35
+ totalMB: int
36
+
37
+
38
+ class AgentSystemInfo(TypedDict):
39
+ cpu: AgentSystemInfoCPU
40
+ memory: AgentSystemInfoMemory
41
+
42
+
43
+ class AgentData(TypedDict):
44
+ systemInfo: AgentSystemInfo
45
+
46
+
47
+ class AgentPersonal(WithUser):
48
+ id: str
49
+ name: str
50
+ status: AgentStatus
51
+ kind: Literal["personal"]
52
+ capabilities: list[AgentCapabilities]
53
+ activities: list[Activity]
54
+ ownerId: str
55
+ createdAt: str
56
+ ip: NotRequired[str]
57
+ version: NotRequired[str]
58
+ imageVersion: NotRequired[str]
59
+ lastSeen: NotRequired[str]
60
+ data: NotRequired[AgentData]
61
+
62
+
63
+ class AgentCloudCPU(TypedDict):
64
+ parallelism: int
65
+
66
+
67
+ class AgentCloudSystemInfo(TypedDict):
68
+ cpu: AgentCloudCPU
69
+ memory: AgentSystemInfoMemory
70
+
71
+
72
+ class AgentCloudData(TypedDict):
73
+ systemInfo: AgentCloudSystemInfo
74
+
75
+
76
+ class AgentCloud(WithUser):
77
+ id: str
78
+ name: str
79
+ status: AgentStatus
80
+ kind: Literal["cloud"]
81
+ capabilities: list[AgentCapabilities]
82
+ activities: list[Activity]
83
+ ownerId: str
84
+ tooltip: NotRequired[str]
85
+ timeout: NotRequired[str]
86
+ data: NotRequired[AgentCloudData]
87
+
88
+
89
+ Agent: TypeAlias = AgentPersonal | AgentCloud
90
+
91
+
92
+ class WithAgent(TypedDict):
93
+ agentId: str
94
+ agentName: NotRequired[str]
95
+
96
+
97
+ class CreateAgentArgs(TypedDict):
98
+ name: str
99
+ kind: AgentKind
100
+ ownerId: str
101
+
102
+
103
+ class CreateAgentResult(TypedDict):
104
+ agent: Agent
105
+ token: str
106
+ cmdLine: str
107
+
108
+
109
+ class ResetAgentTokenResult(TypedDict):
110
+ agent: Agent
111
+ token: str
112
+ cmdLine: str
113
+
114
+
115
+ class DeleteAgentResult(TypedDict, total=False):
116
+ cmdLine: str
117
+
118
+
119
+ class AgentsPage(TypedDict):
120
+ items: list[Agent]
121
+ next: NotRequired[str]
122
+
123
+
124
+ class AgentsClient:
125
+ def __init__(self, transport: ApiTransport) -> None:
126
+ self._transport = transport
127
+
128
+ def list(
129
+ self,
130
+ *,
131
+ for_user_id: str,
132
+ next: str | None = None,
133
+ with_usernames: bool | None = None,
134
+ ) -> AgentsPage:
135
+ return cast(
136
+ AgentsPage,
137
+ self._transport.get_json(
138
+ "v1/agents",
139
+ search_params={
140
+ "forUserId": for_user_id,
141
+ "next": next,
142
+ "withUsernames": with_usernames,
143
+ },
144
+ ),
145
+ )
146
+
147
+ def create(self, args: CreateAgentArgs) -> CreateAgentResult:
148
+ return cast(
149
+ CreateAgentResult, self._transport.post_json("v1/agents", json_data=args)
150
+ )
151
+
152
+ def id(self, agent_id: str) -> "AgentByIdClient":
153
+ return AgentByIdClient(self._transport, agent_id)
154
+
155
+
156
+ class AgentByIdClient:
157
+ def __init__(self, transport: ApiTransport, agent_id: str) -> None:
158
+ self._transport = transport
159
+ self._agent_id = agent_id
160
+
161
+ def get(self, *, with_usernames: bool | None = None) -> Agent:
162
+ return cast(
163
+ Agent,
164
+ self._transport.get_json(
165
+ "v1/agents/:agentId",
166
+ path_params={"agentId": self._agent_id},
167
+ search_params={"withUsernames": with_usernames},
168
+ ),
169
+ )
170
+
171
+ def reset_token(self) -> ResetAgentTokenResult:
172
+ return cast(
173
+ ResetAgentTokenResult,
174
+ self._transport.post_json(
175
+ "v1/agents/:agentId/$resetToken",
176
+ path_params={"agentId": self._agent_id},
177
+ ),
178
+ )
179
+
180
+ def delete(self) -> DeleteAgentResult:
181
+ return cast(
182
+ DeleteAgentResult,
183
+ self._transport.delete_json(
184
+ "v1/agents/:agentId", path_params={"agentId": self._agent_id}
185
+ ),
186
+ )
kotharcomputing/ai.py ADDED
@@ -0,0 +1,262 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Callable, Literal, NotRequired, TypeAlias, TypedDict, cast
4
+
5
+ from .common import JSONValue
6
+ from .errors import ProblemDetails
7
+ from .fetch import ApiTransport
8
+ from .users import WithUser
9
+
10
+
11
+ class AIChatContentText(TypedDict):
12
+ type: Literal["text"]
13
+ text: str
14
+
15
+
16
+ class AIChatFileLocation(TypedDict):
17
+ line: int
18
+ column: int
19
+
20
+
21
+ class AIChatSelection(TypedDict):
22
+ start: AIChatFileLocation
23
+ end: AIChatFileLocation
24
+
25
+
26
+ class AIChatContentEditorContextFile(TypedDict):
27
+ path: str
28
+ content: str
29
+ is_truncated: bool
30
+ language: NotRequired[str]
31
+ is_primary: NotRequired[bool]
32
+ cursor: NotRequired[AIChatFileLocation]
33
+ selection: NotRequired[AIChatSelection]
34
+
35
+
36
+ class AIChatContentEditorContext(TypedDict):
37
+ type: Literal["editor_context"]
38
+ files: list[AIChatContentEditorContextFile]
39
+
40
+
41
+ AIChatMessageAssistantContent: TypeAlias = AIChatContentText
42
+ AIChatMessageUserContent: TypeAlias = AIChatContentText
43
+ AIChatMessageUserContentEdition: TypeAlias = (
44
+ AIChatContentText | AIChatContentEditorContext
45
+ )
46
+
47
+
48
+ class AIChatToolDescriptionFunctionData(TypedDict):
49
+ name: str
50
+ description: NotRequired[str]
51
+ parameters: NotRequired[JSONValue]
52
+
53
+
54
+ class AIChatToolDescriptionFunction(TypedDict):
55
+ type: Literal["function"]
56
+ function: AIChatToolDescriptionFunctionData
57
+
58
+
59
+ AIChatToolDescription: TypeAlias = AIChatToolDescriptionFunction
60
+
61
+
62
+ class AIChatToolCallFunctionData(TypedDict):
63
+ name: str
64
+ arguments: str
65
+
66
+
67
+ class AIChatToolCallFunction(TypedDict):
68
+ id: str
69
+ type: Literal["function"]
70
+ function: AIChatToolCallFunctionData
71
+
72
+
73
+ AIChatToolCall: TypeAlias = AIChatToolCallFunction
74
+
75
+
76
+ class AbbreviatedAIChat(TypedDict):
77
+ id: str
78
+ createdAt: str
79
+ updatedAt: str
80
+ summary: NotRequired[str]
81
+
82
+
83
+ class AIChatMessageApi(TypedDict):
84
+ role: Literal["api"]
85
+ aiChat: AbbreviatedAIChat
86
+
87
+
88
+ class AIChatMessageUser(TypedDict):
89
+ role: Literal["user"]
90
+ content: list[AIChatMessageUserContent]
91
+
92
+
93
+ class AIChatMessageAssistant(TypedDict):
94
+ role: Literal["assistant"]
95
+ content: NotRequired[list[AIChatMessageAssistantContent]]
96
+ toolCalls: NotRequired[list[AIChatToolCall]]
97
+ refusal: NotRequired[str]
98
+ reasoning: NotRequired[str]
99
+
100
+
101
+ class AIChatMessageAssistantDelta(TypedDict):
102
+ role: Literal["assistant_delta"]
103
+ content: NotRequired[str]
104
+ refusal: NotRequired[str]
105
+ reasoning: NotRequired[str]
106
+
107
+
108
+ class AIChatMessageError(TypedDict):
109
+ role: Literal["error"]
110
+ error: ProblemDetails
111
+
112
+
113
+ class AIChatMessageTool(TypedDict):
114
+ role: Literal["tool"]
115
+ toolCallId: str
116
+ content: str
117
+
118
+
119
+ AIChatMessage: TypeAlias = AIChatMessageApi | AIChatMessageUser | AIChatMessageAssistant
120
+ AIChatMessageWithStreaming: TypeAlias = (
121
+ AIChatMessage | AIChatMessageAssistantDelta | AIChatMessageError
122
+ )
123
+
124
+
125
+ class AIChat(WithUser):
126
+ id: str
127
+ createdAt: str
128
+ updatedAt: str
129
+ messages: list[AIChatMessage]
130
+ summary: NotRequired[str]
131
+
132
+
133
+ class AIChatsPage(TypedDict):
134
+ items: list[AbbreviatedAIChat]
135
+ next: NotRequired[str]
136
+
137
+
138
+ class AIModelInfo(TypedDict):
139
+ id: str
140
+ name: str
141
+ default: NotRequired[bool]
142
+
143
+
144
+ class AIChatMessageUserCreate(TypedDict):
145
+ role: Literal["user"]
146
+ content: list[AIChatMessageUserContentEdition]
147
+
148
+
149
+ AIChatMessageUserAppend: TypeAlias = AIChatMessageUserCreate | AIChatMessageTool
150
+
151
+
152
+ class AIChatByIdClient:
153
+ def __init__(
154
+ self, transport: ApiTransport, workspace_id: str, chat_id: str
155
+ ) -> None:
156
+ self._transport = transport
157
+ self._workspace_id = workspace_id
158
+ self._chat_id = chat_id
159
+
160
+ def get(self, *, with_usernames: bool | None = None) -> AIChat:
161
+ return cast(
162
+ AIChat,
163
+ self._transport.get_json(
164
+ "v1/workspaces/:workspaceId/ai/chats/:chatId",
165
+ path_params={
166
+ "workspaceId": self._workspace_id,
167
+ "chatId": self._chat_id,
168
+ },
169
+ search_params={"withUsernames": with_usernames},
170
+ ),
171
+ )
172
+
173
+ def append(
174
+ self,
175
+ *,
176
+ message: AIChatMessageUserAppend,
177
+ on: Callable[[AIChatMessage | AIChatMessageWithStreaming], None],
178
+ model: str | None = None,
179
+ tools: list[AIChatToolDescription] | None = None,
180
+ stream: bool | None = None,
181
+ ) -> None:
182
+ self._transport.sse(
183
+ "put",
184
+ "v1/workspaces/:workspaceId/ai/chats/:chatId",
185
+ path_params={"workspaceId": self._workspace_id, "chatId": self._chat_id},
186
+ json_data={
187
+ "message": message,
188
+ "model": model,
189
+ "tools": tools,
190
+ "stream": stream,
191
+ },
192
+ callback=on,
193
+ )
194
+
195
+ def delete(self) -> None:
196
+ self._transport.raw(
197
+ "delete",
198
+ "v1/workspaces/:workspaceId/ai/chats/:chatId",
199
+ path_params={"workspaceId": self._workspace_id, "chatId": self._chat_id},
200
+ )
201
+
202
+
203
+ class AIChatsClient:
204
+ def __init__(self, transport: ApiTransport, workspace_id: str) -> None:
205
+ self._transport = transport
206
+ self._workspace_id = workspace_id
207
+
208
+ def list(self, *, next: str | None = None) -> AIChatsPage:
209
+ return cast(
210
+ AIChatsPage,
211
+ self._transport.get_json(
212
+ "v1/workspaces/:workspaceId/ai/chats",
213
+ path_params={"workspaceId": self._workspace_id},
214
+ search_params={"next": next},
215
+ ),
216
+ )
217
+
218
+ def create(
219
+ self,
220
+ *,
221
+ message: AIChatMessageUserCreate,
222
+ on: Callable[[AIChatMessage | AIChatMessageWithStreaming], None],
223
+ model: str | None = None,
224
+ tools: list[AIChatToolDescription] | None = None,
225
+ stream: bool | None = None,
226
+ ) -> None:
227
+ self._transport.sse(
228
+ "post",
229
+ "v1/workspaces/:workspaceId/ai/chats",
230
+ path_params={"workspaceId": self._workspace_id},
231
+ json_data={
232
+ "message": message,
233
+ "model": model,
234
+ "tools": tools,
235
+ "stream": stream,
236
+ },
237
+ callback=on,
238
+ )
239
+
240
+ def id(self, chat_id: str) -> AIChatByIdClient:
241
+ return AIChatByIdClient(self._transport, self._workspace_id, chat_id)
242
+
243
+
244
+ class AIModelsClient:
245
+ def __init__(self, transport: ApiTransport, workspace_id: str) -> None:
246
+ self._transport = transport
247
+ self._workspace_id = workspace_id
248
+
249
+ def list(self) -> list[AIModelInfo]:
250
+ return cast(
251
+ list[AIModelInfo],
252
+ self._transport.get_json(
253
+ "v1/workspaces/:workspaceId/ai/models",
254
+ path_params={"workspaceId": self._workspace_id},
255
+ ),
256
+ )
257
+
258
+
259
+ class WorkspaceAIClient:
260
+ def __init__(self, transport: ApiTransport, workspace_id: str) -> None:
261
+ self.chats = AIChatsClient(transport, workspace_id)
262
+ self.models = AIModelsClient(transport, workspace_id)
@@ -0,0 +1,111 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import date, datetime
4
+ from typing import Literal, NotRequired, TypeAlias, TypedDict, cast
5
+
6
+ from .fetch import ApiTransport, to_optional_iso
7
+ from .users import WithUser
8
+
9
+
10
+ class ApiToken(WithUser):
11
+ createdAt: str
12
+ id: str
13
+ name: str
14
+ permissions: "ApiTokenPermissions"
15
+ workspaceId: str
16
+ description: NotRequired[str]
17
+ expiresAt: NotRequired[str]
18
+
19
+
20
+ ApiTokenPermissionSubject: TypeAlias = Literal["agent", "execution", "file", "job"]
21
+ ApiTokenPermissionAction: TypeAlias = Literal[
22
+ "create", "read", "update", "delete", "manage"
23
+ ]
24
+ ApiTokenPermissions: TypeAlias = list[
25
+ tuple[ApiTokenPermissionAction, ApiTokenPermissionSubject]
26
+ ]
27
+
28
+
29
+ class CreateApiTokenArgs(TypedDict):
30
+ name: str
31
+ permissions: ApiTokenPermissions
32
+ description: NotRequired[str]
33
+ expiresAt: NotRequired[str | date | datetime]
34
+
35
+
36
+ class CreateApiTokenResult(TypedDict):
37
+ apiToken: ApiToken
38
+ token: str
39
+
40
+
41
+ class ApiTokensPage(TypedDict):
42
+ items: list[ApiToken]
43
+ next: NotRequired[str]
44
+
45
+
46
+ class ApiTokensByIdClient:
47
+ def __init__(
48
+ self, transport: ApiTransport, workspace_id: str, api_token_id: str
49
+ ) -> None:
50
+ self._transport = transport
51
+ self._workspace_id = workspace_id
52
+ self._api_token_id = api_token_id
53
+
54
+ def get(self, *, with_usernames: bool | None = None) -> ApiToken:
55
+ return cast(
56
+ ApiToken,
57
+ self._transport.get_json(
58
+ "v1/workspaces/:workspaceId/api-tokens/:apiTokenId",
59
+ path_params={
60
+ "workspaceId": self._workspace_id,
61
+ "apiTokenId": self._api_token_id,
62
+ },
63
+ search_params={"withUsernames": with_usernames},
64
+ ),
65
+ )
66
+
67
+ def delete(self) -> None:
68
+ self._transport.raw(
69
+ "delete",
70
+ "v1/workspaces/:workspaceId/api-tokens/:apiTokenId",
71
+ path_params={
72
+ "workspaceId": self._workspace_id,
73
+ "apiTokenId": self._api_token_id,
74
+ },
75
+ )
76
+
77
+
78
+ class ApiTokensClient:
79
+ def __init__(self, transport: ApiTransport, workspace_id: str) -> None:
80
+ self._transport = transport
81
+ self._workspace_id = workspace_id
82
+
83
+ def list(
84
+ self, *, next: str | None = None, with_usernames: bool | None = None
85
+ ) -> ApiTokensPage:
86
+ return cast(
87
+ ApiTokensPage,
88
+ self._transport.get_json(
89
+ "v1/workspaces/:workspaceId/api-tokens",
90
+ path_params={"workspaceId": self._workspace_id},
91
+ search_params={"next": next, "withUsernames": with_usernames},
92
+ ),
93
+ )
94
+
95
+ def create(self, args: CreateApiTokenArgs) -> CreateApiTokenResult:
96
+ payload = dict(args)
97
+ expires_at = payload.get("expiresAt")
98
+ if isinstance(expires_at, (date, datetime)):
99
+ payload["expiresAt"] = to_optional_iso(expires_at)
100
+
101
+ return cast(
102
+ CreateApiTokenResult,
103
+ self._transport.post_json(
104
+ "v1/workspaces/:workspaceId/api-tokens",
105
+ path_params={"workspaceId": self._workspace_id},
106
+ json_data=payload,
107
+ ),
108
+ )
109
+
110
+ def id(self, api_token_id: str) -> ApiTokensByIdClient:
111
+ return ApiTokensByIdClient(self._transport, self._workspace_id, api_token_id)
@@ -0,0 +1,78 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, Mapping
5
+
6
+ from .agents import AgentsClient
7
+ from .fetch import AccessTokenProvider, ApiTransport, QueryParamValue, RawResponse
8
+ from .runtimes import RuntimesClient
9
+ from .service_prices import ServicePricesClient
10
+ from .users import UsersClient
11
+ from .workspaces import WorkspacesClient
12
+
13
+ DEFAULT_BASE_URL = "https://api.kotharcomputing.com"
14
+
15
+
16
+ @dataclass(slots=True)
17
+ class KotharClientOptions:
18
+ base_url: str = DEFAULT_BASE_URL
19
+ access_token: AccessTokenProvider | None = None
20
+ timeout: float = 30.0
21
+
22
+
23
+ class KotharClient:
24
+ def __init__(
25
+ self,
26
+ options: KotharClientOptions | None = None,
27
+ *,
28
+ base_url: str = DEFAULT_BASE_URL,
29
+ access_token: AccessTokenProvider | None = None,
30
+ timeout: float = 30.0,
31
+ ) -> None:
32
+ if options is None:
33
+ options = KotharClientOptions(
34
+ base_url=base_url,
35
+ access_token=access_token,
36
+ timeout=timeout,
37
+ )
38
+
39
+ self._transport = ApiTransport(
40
+ base_url=options.base_url,
41
+ access_token=options.access_token,
42
+ timeout=options.timeout,
43
+ )
44
+
45
+ self.agents = AgentsClient(self._transport)
46
+ self.runtimes = RuntimesClient(self._transport)
47
+ self.service_prices = ServicePricesClient(self._transport)
48
+ self.users = UsersClient(self._transport)
49
+ self.workspaces = WorkspacesClient(self._transport)
50
+
51
+ # TS-compat aliases.
52
+ self.servicePrices = self.service_prices
53
+ self.apiVersion = self.api_version
54
+ self._raw = self.raw
55
+
56
+ def api_version(self) -> dict[str, str]:
57
+ return self._transport.get_json("version")
58
+
59
+ def raw(
60
+ self,
61
+ method: str,
62
+ templated_url: str,
63
+ *,
64
+ path_params: Mapping[str, str | int | bool] | None = None,
65
+ search_params: Mapping[str, QueryParamValue] | None = None,
66
+ headers: Mapping[str, str | None] | None = None,
67
+ body: bytes | str | None = None,
68
+ json_data: Any | None = None,
69
+ ) -> RawResponse:
70
+ return self._transport.raw(
71
+ method,
72
+ templated_url,
73
+ path_params=path_params,
74
+ search_params=search_params,
75
+ headers=headers,
76
+ body=body,
77
+ json_data=json_data,
78
+ )
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TypeAlias
4
+
5
+
6
+ JSONPrimitive: TypeAlias = str | int | float | bool | None
7
+ JSONValue: TypeAlias = JSONPrimitive | list["JSONValue"] | dict[str, "JSONValue"]