ogx-api 1.1.2__tar.gz → 1.2.0__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.
- {ogx_api-1.1.2 → ogx_api-1.2.0}/PKG-INFO +4 -4
- {ogx_api-1.1.2 → ogx_api-1.2.0}/__init__.py +76 -0
- ogx_api-1.2.0/containers/__init__.py +92 -0
- ogx_api-1.2.0/containers/api.py +206 -0
- ogx_api-1.2.0/containers/fastapi_routes.py +205 -0
- ogx_api-1.2.0/containers/models.py +444 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/datatypes.py +4 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/files/fastapi_routes.py +36 -5
- {ogx_api-1.1.2 → ogx_api-1.2.0}/inference/api.py +29 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/internal/sqlstore.py +2 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/ogx_api.egg-info/PKG-INFO +4 -4
- {ogx_api-1.1.2 → ogx_api-1.2.0}/ogx_api.egg-info/SOURCES.txt +8 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/ogx_api.egg-info/requires.txt +3 -3
- {ogx_api-1.1.2 → ogx_api-1.2.0}/openai_responses.py +1 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/pyproject.toml +5 -4
- {ogx_api-1.1.2 → ogx_api-1.2.0}/responses/__init__.py +2 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/responses/models.py +55 -1
- {ogx_api-1.1.2 → ogx_api-1.2.0}/uv.lock +90 -24
- {ogx_api-1.1.2 → ogx_api-1.2.0}/vector_io/api.py +5 -1
- {ogx_api-1.1.2 → ogx_api-1.2.0}/vector_io/fastapi_routes.py +21 -1
- {ogx_api-1.1.2 → ogx_api-1.2.0}/vector_io/models.py +3 -2
- {ogx_api-1.1.2 → ogx_api-1.2.0}/README.md +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/admin/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/admin/api.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/admin/fastapi_routes.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/admin/models.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/batches/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/batches/api.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/batches/fastapi_routes.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/batches/models.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/common/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/common/content_types.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/common/errors.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/common/job_types.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/common/responses.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/common/type_system.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/common/upload_limits.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/connectors/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/connectors/api.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/connectors/models.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/conversations/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/conversations/api.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/conversations/fastapi_routes.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/conversations/models.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/file_processors/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/file_processors/api.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/file_processors/fastapi_routes.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/file_processors/models.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/files/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/files/api.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/files/models.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/filters.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/inference/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/inference/fastapi_routes.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/inference/models.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/inspect_api/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/inspect_api/api.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/inspect_api/fastapi_routes.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/inspect_api/models.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/interactions/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/interactions/api.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/interactions/fastapi_routes.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/interactions/models.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/internal/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/internal/kvstore.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/messages/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/messages/api.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/messages/fastapi_routes.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/messages/models.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/models/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/models/api.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/models/fastapi_routes.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/models/models.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/ogx_api.egg-info/dependency_links.txt +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/ogx_api.egg-info/top_level.txt +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/prompts/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/prompts/api.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/prompts/fastapi_routes.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/prompts/models.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/provider/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/providers/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/providers/api.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/providers/fastapi_routes.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/providers/models.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/py.typed +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/rag_tool.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/resource.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/responses/api.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/responses/fastapi_routes.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/router_utils.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/schema_utils.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/setup.cfg +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/skills/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/skills/api.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/skills/fastapi_routes.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/skills/models.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/tools/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/tools/api.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/tools/models.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/types/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/validators.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/vector_io/__init__.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/vector_stores.py +0 -0
- {ogx_api-1.1.2 → ogx_api-1.2.0}/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ogx-api
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: API and Provider specifications for OGX - lightweight package with protocol definitions and provider specs
|
|
5
5
|
Author-email: The OGX Contributors <contributors@ogx.dev>
|
|
6
6
|
License: MIT
|
|
@@ -15,12 +15,12 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
|
15
15
|
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
16
16
|
Requires-Python: >=3.12
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
|
-
Requires-Dist: openai>=2.
|
|
18
|
+
Requires-Dist: openai>=2.43.0
|
|
19
19
|
Requires-Dist: fastapi<1.0,>=0.136.3
|
|
20
20
|
Requires-Dist: pydantic>=2.11.9
|
|
21
21
|
Requires-Dist: jsonschema>=4.26.0
|
|
22
|
-
Requires-Dist: opentelemetry-sdk>=1.
|
|
23
|
-
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.
|
|
22
|
+
Requires-Dist: opentelemetry-sdk>=1.43.0
|
|
23
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.43.0
|
|
24
24
|
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.42.1
|
|
25
25
|
|
|
26
26
|
# ogx-api
|
|
@@ -64,6 +64,7 @@ from .responses import (
|
|
|
64
64
|
DeleteResponseRequest,
|
|
65
65
|
ListResponseInputItemsRequest,
|
|
66
66
|
ListResponsesRequest,
|
|
67
|
+
MemoryToolConfig,
|
|
67
68
|
ResponseItemInclude,
|
|
68
69
|
ResponseTruncation,
|
|
69
70
|
ResponseStreamOptions,
|
|
@@ -130,6 +131,44 @@ from .connectors import (
|
|
|
130
131
|
GetConnectorToolRequest,
|
|
131
132
|
ListConnectorToolsRequest,
|
|
132
133
|
)
|
|
134
|
+
from .containers import (
|
|
135
|
+
Container,
|
|
136
|
+
ContainerCreateRequest,
|
|
137
|
+
ContainerDeleteResponse,
|
|
138
|
+
ContainerExpiresAfter,
|
|
139
|
+
ContainerFile,
|
|
140
|
+
ContainerFileDeleteResponse,
|
|
141
|
+
ContainerFileSource,
|
|
142
|
+
ContainerRuntime,
|
|
143
|
+
Containers,
|
|
144
|
+
ContainerStatus,
|
|
145
|
+
DeleteContainerFileRequest,
|
|
146
|
+
DeleteContainerRequest,
|
|
147
|
+
ExecuteShellRequest,
|
|
148
|
+
GetContainerFileContentRequest,
|
|
149
|
+
GetContainerFileRequest,
|
|
150
|
+
GetContainerRequest,
|
|
151
|
+
ListContainerFilesRequest,
|
|
152
|
+
ListContainerFilesResponse,
|
|
153
|
+
ListContainersRequest,
|
|
154
|
+
ListContainersResponse,
|
|
155
|
+
MountSkillsRequest,
|
|
156
|
+
NetworkCredential,
|
|
157
|
+
NetworkDomainCredential,
|
|
158
|
+
NetworkPolicy,
|
|
159
|
+
NetworkPolicyExtended,
|
|
160
|
+
NetworkPolicyMode,
|
|
161
|
+
ShellCallOutput,
|
|
162
|
+
ShellEnvironment,
|
|
163
|
+
ShellEnvironmentContainerAuto,
|
|
164
|
+
ShellEnvironmentContainerReference,
|
|
165
|
+
ShellEnvironmentLocal,
|
|
166
|
+
ShellOutcome,
|
|
167
|
+
ShellOutcomeFailure,
|
|
168
|
+
ShellOutcomeSuccess,
|
|
169
|
+
ShellOutcomeTimeout,
|
|
170
|
+
UploadContainerFileRequest,
|
|
171
|
+
)
|
|
133
172
|
from .conversations import (
|
|
134
173
|
AddItemsRequest,
|
|
135
174
|
Conversation,
|
|
@@ -550,6 +589,7 @@ __all__ = [
|
|
|
550
589
|
"DeleteResponseRequest",
|
|
551
590
|
"ListResponseInputItemsRequest",
|
|
552
591
|
"ListResponsesRequest",
|
|
592
|
+
"MemoryToolConfig",
|
|
553
593
|
"RetrieveResponseRequest",
|
|
554
594
|
"AllowedToolsFilter",
|
|
555
595
|
"Api",
|
|
@@ -587,6 +627,42 @@ __all__ = [
|
|
|
587
627
|
"ConnectorInput",
|
|
588
628
|
"Connectors",
|
|
589
629
|
"ConnectorType",
|
|
630
|
+
"Container",
|
|
631
|
+
"ContainerCreateRequest",
|
|
632
|
+
"ContainerDeleteResponse",
|
|
633
|
+
"ContainerExpiresAfter",
|
|
634
|
+
"ContainerFile",
|
|
635
|
+
"ContainerFileDeleteResponse",
|
|
636
|
+
"ContainerFileSource",
|
|
637
|
+
"ContainerRuntime",
|
|
638
|
+
"Containers",
|
|
639
|
+
"ContainerStatus",
|
|
640
|
+
"DeleteContainerFileRequest",
|
|
641
|
+
"DeleteContainerRequest",
|
|
642
|
+
"ExecuteShellRequest",
|
|
643
|
+
"GetContainerFileContentRequest",
|
|
644
|
+
"GetContainerFileRequest",
|
|
645
|
+
"GetContainerRequest",
|
|
646
|
+
"ListContainerFilesRequest",
|
|
647
|
+
"ListContainerFilesResponse",
|
|
648
|
+
"ListContainersRequest",
|
|
649
|
+
"ListContainersResponse",
|
|
650
|
+
"MountSkillsRequest",
|
|
651
|
+
"NetworkCredential",
|
|
652
|
+
"NetworkDomainCredential",
|
|
653
|
+
"NetworkPolicy",
|
|
654
|
+
"NetworkPolicyExtended",
|
|
655
|
+
"NetworkPolicyMode",
|
|
656
|
+
"ShellCallOutput",
|
|
657
|
+
"ShellEnvironment",
|
|
658
|
+
"ShellEnvironmentContainerAuto",
|
|
659
|
+
"ShellEnvironmentContainerReference",
|
|
660
|
+
"ShellEnvironmentLocal",
|
|
661
|
+
"ShellOutcome",
|
|
662
|
+
"ShellOutcomeFailure",
|
|
663
|
+
"ShellOutcomeSuccess",
|
|
664
|
+
"ShellOutcomeTimeout",
|
|
665
|
+
"UploadContainerFileRequest",
|
|
590
666
|
"AddItemsRequest",
|
|
591
667
|
"Conversation",
|
|
592
668
|
"ConversationDeletedResource",
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Copyright (c) The OGX Contributors.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# This source code is licensed under the terms described in the LICENSE file in
|
|
5
|
+
# the root directory of this source tree.
|
|
6
|
+
|
|
7
|
+
"""Containers API module.
|
|
8
|
+
|
|
9
|
+
Provides CRUD over sandboxed execution environments used by the Responses
|
|
10
|
+
``shell`` and ``code_interpreter`` tools. Protocol definitions are in
|
|
11
|
+
``api.py``, Pydantic models in ``models.py``, and FastAPI routes in
|
|
12
|
+
``fastapi_routes.py``.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from . import fastapi_routes
|
|
16
|
+
from .api import ContainerRuntime, Containers
|
|
17
|
+
from .models import (
|
|
18
|
+
Container,
|
|
19
|
+
ContainerCreateRequest,
|
|
20
|
+
ContainerDeleteResponse,
|
|
21
|
+
ContainerExpiresAfter,
|
|
22
|
+
ContainerFile,
|
|
23
|
+
ContainerFileDeleteResponse,
|
|
24
|
+
ContainerFileSource,
|
|
25
|
+
ContainerStatus,
|
|
26
|
+
DeleteContainerFileRequest,
|
|
27
|
+
DeleteContainerRequest,
|
|
28
|
+
ExecuteShellRequest,
|
|
29
|
+
GetContainerFileContentRequest,
|
|
30
|
+
GetContainerFileRequest,
|
|
31
|
+
GetContainerRequest,
|
|
32
|
+
ListContainerFilesRequest,
|
|
33
|
+
ListContainerFilesResponse,
|
|
34
|
+
ListContainersRequest,
|
|
35
|
+
ListContainersResponse,
|
|
36
|
+
MountSkillsRequest,
|
|
37
|
+
NetworkCredential,
|
|
38
|
+
NetworkDomainCredential,
|
|
39
|
+
NetworkPolicy,
|
|
40
|
+
NetworkPolicyExtended,
|
|
41
|
+
NetworkPolicyMode,
|
|
42
|
+
ShellCallOutput,
|
|
43
|
+
ShellEnvironment,
|
|
44
|
+
ShellEnvironmentContainerAuto,
|
|
45
|
+
ShellEnvironmentContainerReference,
|
|
46
|
+
ShellEnvironmentLocal,
|
|
47
|
+
ShellOutcome,
|
|
48
|
+
ShellOutcomeFailure,
|
|
49
|
+
ShellOutcomeSuccess,
|
|
50
|
+
ShellOutcomeTimeout,
|
|
51
|
+
UploadContainerFileRequest,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
__all__ = [
|
|
55
|
+
"Container",
|
|
56
|
+
"ContainerCreateRequest",
|
|
57
|
+
"ContainerDeleteResponse",
|
|
58
|
+
"ContainerExpiresAfter",
|
|
59
|
+
"ContainerFile",
|
|
60
|
+
"ContainerFileDeleteResponse",
|
|
61
|
+
"ContainerFileSource",
|
|
62
|
+
"ContainerRuntime",
|
|
63
|
+
"ContainerStatus",
|
|
64
|
+
"Containers",
|
|
65
|
+
"DeleteContainerFileRequest",
|
|
66
|
+
"DeleteContainerRequest",
|
|
67
|
+
"ExecuteShellRequest",
|
|
68
|
+
"GetContainerFileContentRequest",
|
|
69
|
+
"GetContainerFileRequest",
|
|
70
|
+
"GetContainerRequest",
|
|
71
|
+
"ListContainerFilesRequest",
|
|
72
|
+
"ListContainerFilesResponse",
|
|
73
|
+
"ListContainersRequest",
|
|
74
|
+
"ListContainersResponse",
|
|
75
|
+
"MountSkillsRequest",
|
|
76
|
+
"NetworkCredential",
|
|
77
|
+
"NetworkDomainCredential",
|
|
78
|
+
"NetworkPolicy",
|
|
79
|
+
"NetworkPolicyExtended",
|
|
80
|
+
"NetworkPolicyMode",
|
|
81
|
+
"ShellCallOutput",
|
|
82
|
+
"ShellEnvironment",
|
|
83
|
+
"ShellEnvironmentContainerAuto",
|
|
84
|
+
"ShellEnvironmentContainerReference",
|
|
85
|
+
"ShellEnvironmentLocal",
|
|
86
|
+
"ShellOutcome",
|
|
87
|
+
"ShellOutcomeFailure",
|
|
88
|
+
"ShellOutcomeSuccess",
|
|
89
|
+
"ShellOutcomeTimeout",
|
|
90
|
+
"UploadContainerFileRequest",
|
|
91
|
+
"fastapi_routes",
|
|
92
|
+
]
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Copyright (c) The OGX Contributors.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# This source code is licensed under the terms described in the LICENSE file in
|
|
5
|
+
# the root directory of this source tree.
|
|
6
|
+
|
|
7
|
+
"""Containers API protocol definitions.
|
|
8
|
+
|
|
9
|
+
This module contains two protocols:
|
|
10
|
+
|
|
11
|
+
* ``Containers`` — the HTTP-facing CRUD API for sandboxed execution
|
|
12
|
+
environments.
|
|
13
|
+
* ``ContainerRuntime`` — the internal provider protocol that backends
|
|
14
|
+
(Docker/Podman, Kubernetes, local) implement. The Containers provider
|
|
15
|
+
delegates to a ``ContainerRuntime`` for the actual lifecycle, file, and
|
|
16
|
+
shell-execution work.
|
|
17
|
+
|
|
18
|
+
Pydantic models are defined in ``models.py`` and FastAPI routes in
|
|
19
|
+
``fastapi_routes.py``.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from typing import Protocol, runtime_checkable
|
|
23
|
+
|
|
24
|
+
from fastapi import Response, UploadFile
|
|
25
|
+
|
|
26
|
+
from .models import (
|
|
27
|
+
Container,
|
|
28
|
+
ContainerCreateRequest,
|
|
29
|
+
ContainerDeleteResponse,
|
|
30
|
+
ContainerFile,
|
|
31
|
+
ContainerFileDeleteResponse,
|
|
32
|
+
DeleteContainerFileRequest,
|
|
33
|
+
DeleteContainerRequest,
|
|
34
|
+
ExecuteShellRequest,
|
|
35
|
+
GetContainerFileContentRequest,
|
|
36
|
+
GetContainerFileRequest,
|
|
37
|
+
GetContainerRequest,
|
|
38
|
+
ListContainerFilesRequest,
|
|
39
|
+
ListContainerFilesResponse,
|
|
40
|
+
ListContainersRequest,
|
|
41
|
+
ListContainersResponse,
|
|
42
|
+
MountSkillsRequest,
|
|
43
|
+
ShellCallOutput,
|
|
44
|
+
UploadContainerFileRequest,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@runtime_checkable
|
|
49
|
+
class Containers(Protocol):
|
|
50
|
+
"""HTTP API for managing sandboxed execution containers.
|
|
51
|
+
|
|
52
|
+
Implementations enforce policy layering (request-supplied
|
|
53
|
+
``NetworkPolicyExtended`` cannot expand operator defaults) and then
|
|
54
|
+
delegate to a configured :class:`ContainerRuntime` for backend work.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
async def create_container(
|
|
58
|
+
self,
|
|
59
|
+
request: ContainerCreateRequest,
|
|
60
|
+
) -> Container: ...
|
|
61
|
+
|
|
62
|
+
async def list_containers(
|
|
63
|
+
self,
|
|
64
|
+
request: ListContainersRequest,
|
|
65
|
+
) -> ListContainersResponse: ...
|
|
66
|
+
|
|
67
|
+
async def get_container(
|
|
68
|
+
self,
|
|
69
|
+
request: GetContainerRequest,
|
|
70
|
+
) -> Container: ...
|
|
71
|
+
|
|
72
|
+
async def delete_container(
|
|
73
|
+
self,
|
|
74
|
+
request: DeleteContainerRequest,
|
|
75
|
+
) -> ContainerDeleteResponse: ...
|
|
76
|
+
|
|
77
|
+
async def upload_container_file(
|
|
78
|
+
self,
|
|
79
|
+
request: UploadContainerFileRequest,
|
|
80
|
+
file: UploadFile,
|
|
81
|
+
) -> ContainerFile: ...
|
|
82
|
+
|
|
83
|
+
async def list_container_files(
|
|
84
|
+
self,
|
|
85
|
+
request: ListContainerFilesRequest,
|
|
86
|
+
) -> ListContainerFilesResponse: ...
|
|
87
|
+
|
|
88
|
+
async def get_container_file(
|
|
89
|
+
self,
|
|
90
|
+
request: GetContainerFileRequest,
|
|
91
|
+
) -> ContainerFile: ...
|
|
92
|
+
|
|
93
|
+
async def get_container_file_content(
|
|
94
|
+
self,
|
|
95
|
+
request: GetContainerFileContentRequest,
|
|
96
|
+
) -> Response: ...
|
|
97
|
+
|
|
98
|
+
async def delete_container_file(
|
|
99
|
+
self,
|
|
100
|
+
request: DeleteContainerFileRequest,
|
|
101
|
+
) -> ContainerFileDeleteResponse: ...
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@runtime_checkable
|
|
105
|
+
class ContainerRuntime(Protocol):
|
|
106
|
+
"""Internal provider protocol for container backends.
|
|
107
|
+
|
|
108
|
+
A ``ContainerRuntime`` provider is the thin layer between the Containers
|
|
109
|
+
API and a concrete backend (Docker/Podman socket, Kubernetes API, local
|
|
110
|
+
process supervisor). The Responses ``shell`` tool also calls
|
|
111
|
+
``execute_shell`` directly when running in ``container_auto`` /
|
|
112
|
+
``container_reference`` modes.
|
|
113
|
+
|
|
114
|
+
This protocol is not exposed over HTTP.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
# --- lifecycle -------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
async def create_container(
|
|
120
|
+
self,
|
|
121
|
+
request: ContainerCreateRequest,
|
|
122
|
+
) -> Container:
|
|
123
|
+
"""Create and start a container. Raises if resource limits are exceeded."""
|
|
124
|
+
...
|
|
125
|
+
|
|
126
|
+
async def get_container(self, request: GetContainerRequest) -> Container:
|
|
127
|
+
"""Fetch the current state of a container, including ``last_active_at``."""
|
|
128
|
+
...
|
|
129
|
+
|
|
130
|
+
async def list_containers(
|
|
131
|
+
self,
|
|
132
|
+
request: ListContainersRequest,
|
|
133
|
+
) -> ListContainersResponse:
|
|
134
|
+
"""List containers known to this runtime."""
|
|
135
|
+
...
|
|
136
|
+
|
|
137
|
+
async def delete_container(self, request: DeleteContainerRequest) -> ContainerDeleteResponse:
|
|
138
|
+
"""Stop and remove a container along with its filesystem."""
|
|
139
|
+
...
|
|
140
|
+
|
|
141
|
+
# --- file management ------------------------------------------------
|
|
142
|
+
|
|
143
|
+
async def upload_file(
|
|
144
|
+
self,
|
|
145
|
+
request: UploadContainerFileRequest,
|
|
146
|
+
file: UploadFile,
|
|
147
|
+
) -> ContainerFile:
|
|
148
|
+
"""Copy an uploaded file into the container at the runtime-chosen path."""
|
|
149
|
+
...
|
|
150
|
+
|
|
151
|
+
async def list_files(
|
|
152
|
+
self,
|
|
153
|
+
request: ListContainerFilesRequest,
|
|
154
|
+
) -> ListContainerFilesResponse:
|
|
155
|
+
"""List files tracked inside a container."""
|
|
156
|
+
...
|
|
157
|
+
|
|
158
|
+
async def get_file(
|
|
159
|
+
self,
|
|
160
|
+
request: GetContainerFileRequest,
|
|
161
|
+
) -> ContainerFile:
|
|
162
|
+
"""Get metadata for a single file inside a container."""
|
|
163
|
+
...
|
|
164
|
+
|
|
165
|
+
async def get_file_content(
|
|
166
|
+
self,
|
|
167
|
+
request: GetContainerFileContentRequest,
|
|
168
|
+
) -> Response:
|
|
169
|
+
"""Stream the bytes of a file inside a container."""
|
|
170
|
+
...
|
|
171
|
+
|
|
172
|
+
async def delete_file(
|
|
173
|
+
self,
|
|
174
|
+
request: DeleteContainerFileRequest,
|
|
175
|
+
) -> ContainerFileDeleteResponse:
|
|
176
|
+
"""Remove a file from a container's filesystem."""
|
|
177
|
+
...
|
|
178
|
+
|
|
179
|
+
# --- execution ------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
async def execute_shell(
|
|
182
|
+
self,
|
|
183
|
+
request: ExecuteShellRequest,
|
|
184
|
+
) -> ShellCallOutput:
|
|
185
|
+
"""Run a shell command inside the container and capture its output.
|
|
186
|
+
|
|
187
|
+
``request.command`` is passed as ``argv`` (no shell expansion) to keep
|
|
188
|
+
the interface uniform across backends. Implementations are expected to
|
|
189
|
+
update ``last_active_at`` on the container as a side effect.
|
|
190
|
+
"""
|
|
191
|
+
...
|
|
192
|
+
|
|
193
|
+
# --- skill mounting -------------------------------------------------
|
|
194
|
+
|
|
195
|
+
async def mount_skills(
|
|
196
|
+
self,
|
|
197
|
+
request: MountSkillsRequest,
|
|
198
|
+
) -> None:
|
|
199
|
+
"""Mount skill bundles into ``/mnt/skills/{skill_name}/`` inside the container.
|
|
200
|
+
|
|
201
|
+
Each entry of ``request.skill_bundles`` is ``(skill_name, zip_bytes)``
|
|
202
|
+
— the zip archive is extracted into the named directory. Calling this
|
|
203
|
+
method multiple times with overlapping names overwrites the previous
|
|
204
|
+
contents.
|
|
205
|
+
"""
|
|
206
|
+
...
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Copyright (c) The OGX Contributors.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# This source code is licensed under the terms described in the LICENSE file in
|
|
5
|
+
# the root directory of this source tree.
|
|
6
|
+
|
|
7
|
+
"""FastAPI routes for the Containers API.
|
|
8
|
+
|
|
9
|
+
Endpoints are mounted at ``/v1alpha/containers``. See
|
|
10
|
+
:class:`ogx_api.containers.api.Containers` for the protocol implementations
|
|
11
|
+
must satisfy.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from typing import Annotated
|
|
15
|
+
|
|
16
|
+
from fastapi import APIRouter, Depends, UploadFile
|
|
17
|
+
from fastapi.param_functions import File
|
|
18
|
+
from fastapi.responses import Response
|
|
19
|
+
|
|
20
|
+
from ogx_api.common.upload_limits import (
|
|
21
|
+
DEFAULT_MAX_UPLOAD_SIZE_BYTES,
|
|
22
|
+
PreReadUploadFile,
|
|
23
|
+
read_upload_with_size_limit,
|
|
24
|
+
)
|
|
25
|
+
from ogx_api.router_utils import create_path_dependency, create_query_dependency, standard_responses
|
|
26
|
+
from ogx_api.version import OGX_API_V1ALPHA
|
|
27
|
+
|
|
28
|
+
from .api import Containers
|
|
29
|
+
from .models import (
|
|
30
|
+
Container,
|
|
31
|
+
ContainerCreateRequest,
|
|
32
|
+
ContainerDeleteResponse,
|
|
33
|
+
ContainerFile,
|
|
34
|
+
ContainerFileDeleteResponse,
|
|
35
|
+
DeleteContainerFileRequest,
|
|
36
|
+
DeleteContainerRequest,
|
|
37
|
+
GetContainerFileContentRequest,
|
|
38
|
+
GetContainerFileRequest,
|
|
39
|
+
GetContainerRequest,
|
|
40
|
+
ListContainerFilesRequest,
|
|
41
|
+
ListContainerFilesResponse,
|
|
42
|
+
ListContainersRequest,
|
|
43
|
+
ListContainersResponse,
|
|
44
|
+
UploadContainerFileRequest,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
get_list_containers_request = create_query_dependency(ListContainersRequest)
|
|
48
|
+
get_container_request = create_path_dependency(GetContainerRequest)
|
|
49
|
+
get_delete_container_request = create_path_dependency(DeleteContainerRequest)
|
|
50
|
+
get_upload_container_file_request = create_path_dependency(UploadContainerFileRequest)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# Multi-field path-parameter models cannot use create_path_dependency, which only
|
|
54
|
+
# supports single-field models. The helpers below combine path (+ query) params
|
|
55
|
+
# into the request model explicitly.
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _list_container_files_request(
|
|
59
|
+
container_id: str,
|
|
60
|
+
after: str | None = None,
|
|
61
|
+
limit: int | None = 20,
|
|
62
|
+
order: str | None = "desc",
|
|
63
|
+
) -> ListContainerFilesRequest:
|
|
64
|
+
return ListContainerFilesRequest.model_validate(
|
|
65
|
+
{"container_id": container_id, "after": after, "limit": limit, "order": order}
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _get_container_file_request(container_id: str, file_id: str) -> GetContainerFileRequest:
|
|
70
|
+
return GetContainerFileRequest(container_id=container_id, file_id=file_id)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _get_container_file_content_request(container_id: str, file_id: str) -> GetContainerFileContentRequest:
|
|
74
|
+
return GetContainerFileContentRequest(container_id=container_id, file_id=file_id)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _delete_container_file_request(container_id: str, file_id: str) -> DeleteContainerFileRequest:
|
|
78
|
+
return DeleteContainerFileRequest(container_id=container_id, file_id=file_id)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def create_router(impl: Containers, max_upload_size_bytes: int = DEFAULT_MAX_UPLOAD_SIZE_BYTES) -> APIRouter:
|
|
82
|
+
router = APIRouter(
|
|
83
|
+
prefix=f"/{OGX_API_V1ALPHA}",
|
|
84
|
+
tags=["Containers"],
|
|
85
|
+
responses=standard_responses,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
@router.post(
|
|
89
|
+
"/containers",
|
|
90
|
+
response_model=Container,
|
|
91
|
+
summary="Create container",
|
|
92
|
+
description="Create a sandboxed container for shell/code execution.",
|
|
93
|
+
responses={200: {"description": "The created container."}},
|
|
94
|
+
)
|
|
95
|
+
async def create_container(request: ContainerCreateRequest) -> Container:
|
|
96
|
+
return await impl.create_container(request)
|
|
97
|
+
|
|
98
|
+
@router.get(
|
|
99
|
+
"/containers",
|
|
100
|
+
response_model=ListContainersResponse,
|
|
101
|
+
summary="List containers",
|
|
102
|
+
description="List containers.",
|
|
103
|
+
responses={200: {"description": "The list of containers."}},
|
|
104
|
+
)
|
|
105
|
+
async def list_containers(
|
|
106
|
+
request: Annotated[ListContainersRequest, Depends(get_list_containers_request)],
|
|
107
|
+
) -> ListContainersResponse:
|
|
108
|
+
return await impl.list_containers(request)
|
|
109
|
+
|
|
110
|
+
@router.get(
|
|
111
|
+
"/containers/{container_id}",
|
|
112
|
+
response_model=Container,
|
|
113
|
+
summary="Get container",
|
|
114
|
+
description="Get a container by ID.",
|
|
115
|
+
responses={200: {"description": "The container."}},
|
|
116
|
+
)
|
|
117
|
+
async def get_container(
|
|
118
|
+
request: Annotated[GetContainerRequest, Depends(get_container_request)],
|
|
119
|
+
) -> Container:
|
|
120
|
+
return await impl.get_container(request)
|
|
121
|
+
|
|
122
|
+
@router.delete(
|
|
123
|
+
"/containers/{container_id}",
|
|
124
|
+
response_model=ContainerDeleteResponse,
|
|
125
|
+
summary="Delete container",
|
|
126
|
+
description="Stop and remove a container.",
|
|
127
|
+
responses={200: {"description": "The container was deleted."}},
|
|
128
|
+
)
|
|
129
|
+
async def delete_container(
|
|
130
|
+
request: Annotated[DeleteContainerRequest, Depends(get_delete_container_request)],
|
|
131
|
+
) -> ContainerDeleteResponse:
|
|
132
|
+
return await impl.delete_container(request)
|
|
133
|
+
|
|
134
|
+
@router.post(
|
|
135
|
+
"/containers/{container_id}/files",
|
|
136
|
+
response_model=ContainerFile,
|
|
137
|
+
summary="Upload container file",
|
|
138
|
+
description="Upload a file into a container's filesystem.",
|
|
139
|
+
responses={200: {"description": "The uploaded container file."}},
|
|
140
|
+
)
|
|
141
|
+
async def upload_container_file(
|
|
142
|
+
container_id: str,
|
|
143
|
+
file: Annotated[UploadFile, File(description="The file to upload into the container.")],
|
|
144
|
+
) -> ContainerFile:
|
|
145
|
+
content = await read_upload_with_size_limit(file, max_upload_size_bytes)
|
|
146
|
+
safe_file = PreReadUploadFile(content, filename=file.filename, content_type=file.content_type)
|
|
147
|
+
return await impl.upload_container_file(
|
|
148
|
+
UploadContainerFileRequest(container_id=container_id),
|
|
149
|
+
safe_file,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
@router.get(
|
|
153
|
+
"/containers/{container_id}/files",
|
|
154
|
+
response_model=ListContainerFilesResponse,
|
|
155
|
+
summary="List container files",
|
|
156
|
+
description="List files inside a container.",
|
|
157
|
+
responses={200: {"description": "The list of files."}},
|
|
158
|
+
)
|
|
159
|
+
async def list_container_files(
|
|
160
|
+
request: Annotated[ListContainerFilesRequest, Depends(_list_container_files_request)],
|
|
161
|
+
) -> ListContainerFilesResponse:
|
|
162
|
+
return await impl.list_container_files(request)
|
|
163
|
+
|
|
164
|
+
@router.get(
|
|
165
|
+
"/containers/{container_id}/files/{file_id}",
|
|
166
|
+
response_model=ContainerFile,
|
|
167
|
+
summary="Get container file",
|
|
168
|
+
description="Get metadata for a file inside a container.",
|
|
169
|
+
responses={200: {"description": "The container file metadata."}},
|
|
170
|
+
)
|
|
171
|
+
async def get_container_file(
|
|
172
|
+
request: Annotated[GetContainerFileRequest, Depends(_get_container_file_request)],
|
|
173
|
+
) -> ContainerFile:
|
|
174
|
+
return await impl.get_container_file(request)
|
|
175
|
+
|
|
176
|
+
@router.get(
|
|
177
|
+
"/containers/{container_id}/files/{file_id}/content",
|
|
178
|
+
status_code=200,
|
|
179
|
+
summary="Get container file content",
|
|
180
|
+
description="Download the contents of a file inside a container.",
|
|
181
|
+
responses={
|
|
182
|
+
200: {
|
|
183
|
+
"description": "The file content.",
|
|
184
|
+
"content": {"application/octet-stream": {"schema": {"type": "string", "format": "binary"}}},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
)
|
|
188
|
+
async def get_container_file_content(
|
|
189
|
+
request: Annotated[GetContainerFileContentRequest, Depends(_get_container_file_content_request)],
|
|
190
|
+
) -> Response:
|
|
191
|
+
return await impl.get_container_file_content(request)
|
|
192
|
+
|
|
193
|
+
@router.delete(
|
|
194
|
+
"/containers/{container_id}/files/{file_id}",
|
|
195
|
+
response_model=ContainerFileDeleteResponse,
|
|
196
|
+
summary="Delete container file",
|
|
197
|
+
description="Remove a file from a container's filesystem.",
|
|
198
|
+
responses={200: {"description": "The file was deleted."}},
|
|
199
|
+
)
|
|
200
|
+
async def delete_container_file(
|
|
201
|
+
request: Annotated[DeleteContainerFileRequest, Depends(_delete_container_file_request)],
|
|
202
|
+
) -> ContainerFileDeleteResponse:
|
|
203
|
+
return await impl.delete_container_file(request)
|
|
204
|
+
|
|
205
|
+
return router
|