Skip to content

Commit 0c413bc

Browse files
Merge branch 'main' into feature/add-unstructured-api-provider
2 parents 9948c2d + 6618aca commit 0c413bc

22 files changed

Lines changed: 135 additions & 66 deletions

File tree

.github/workflows/publish-openapi-sdk.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ jobs:
124124
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
125125

126126
- name: Upload build artifacts
127-
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v4.6.0
127+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v4.6.0
128128
with:
129129
name: python-sdk-dist
130130
path: client-sdks/openapi/sdks/python/dist/

ARCHITECTURE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ The full list of auto-routed pairs is defined in `builtin_automatically_routed_a
115115

116116
The `ogx_api` package defines all public-facing types and protocols:
117117

118-
- **Protocols** -- Python `Protocol` classes like `Inference`, `Responses` that define the API contract. HTTP routes are defined via FastAPI routers in `fastapi_routes.py` modules.
118+
- **Protocols** -- Python `Protocol` classes like `Inference`, `Responses`, `Skills` that define the API contract. HTTP routes are defined via FastAPI routers in `fastapi_routes.py` modules.
119119
- **Data Types** -- Pydantic models for requests, responses, and resources (e.g., `Model`, `VectorStore`, `ChatCompletionRequest`).
120120
- **Provider Specs** -- `InlineProviderSpec`, `RemoteProviderSpec`, and related types that define how providers are declared.
121121
- **Internal utilities** -- KVStore and SqlStore abstract interfaces live here so third-party providers can use them without depending on the full server.
@@ -152,7 +152,7 @@ storage:
152152
| PostgreSQL| `PostgresKVStoreConfig` | Production deployments |
153153
| MongoDB | `MongoDBKVStoreConfig` | Document-oriented |
154154

155-
Used by: distribution registry, quota tracking, provider state.
155+
Used by: distribution registry, quota tracking, provider state, skills metadata.
156156

157157
### SqlStore
158158

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ response = client.chat.completions.create(
4141
- **Responses API** — server-side agentic orchestration with tool calling, MCP server integration, and built-in file search (RAG) in a single API call ([learn more](https://ogx-ai.github.io/docs/api-openai))
4242
- **Vector Stores & Files**`/v1/vector_stores` and `/v1/files` for managed document storage and search
4343
- **Batches**`/v1/batches` for offline batch processing
44+
- **Skills**`/v1alpha/skills` for managing versioned skill bundles (zip archives with SKILL.md manifests) that agents can invoke
4445
- **[Open Responses](https://www.openresponses.org/) conformant** — the Responses API implementation passes the Open Responses conformance test suite
4546
- **Multi-SDK support** — use the [Anthropic SDK](https://docs.anthropic.com/en/api/messages) (`/v1/messages`) or [Google GenAI SDK](https://ai.google.dev/gemini-api/docs/interactions) (`/v1alpha/interactions`) natively alongside the OpenAI API
4647

pyproject.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ requires = ["setuptools>=61.0", "setuptools-scm>=8.0"]
33
build-backend = "setuptools.build_meta"
44

55
[tool.setuptools_scm]
6-
fallback_version = "1.0.3.dev0"
6+
fallback_version = "1.1.1.dev0"
77

88
[tool.uv]
99
required-version = ">=0.7.0"
@@ -80,7 +80,7 @@ dependencies = [
8080

8181
[project.optional-dependencies]
8282
client = [
83-
"ogx-client>=1.0.2", # Optional for library-only usage
83+
"ogx-client>=1.1.0", # Optional for library-only usage
8484
]
8585
starter = [
8686
"aiohttp",
@@ -145,7 +145,7 @@ dev = [
145145
"pre-commit>=4.4.0",
146146
"ruamel.yaml", # needed for openapi generator
147147
"openapi-spec-validator>=0.9.0",
148-
"ogx-client>=1.0.2",
148+
"ogx-client>=1.1.0",
149149
"boto3>=1.43.18",
150150
"torch>=2.6.0",
151151
]
@@ -184,7 +184,7 @@ type_checking = [
184184
"langchain-openai>=1.2.2",
185185
"langchain-core",
186186
"langgraph",
187-
"ogx-client>=1.0.2",
187+
"ogx-client>=1.1.0",
188188
]
189189
test-common = [
190190
"aiohttp",

src/ogx/cli/stack/lets_go.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ def _add_file_search_and_responses(run_config: StackConfig) -> None:
248248
("remote::brave-search", "brave-search"),
249249
("remote::tavily-search", "tavily-search"),
250250
("remote::bing-search", "bing-search"),
251+
("remote::nimble-search", "nimble-search"),
251252
]
252253
tool_runtime_registry = get_provider_registry().get(Api.tool_runtime, {})
253254
existing_web_search: set[str] = {

src/ogx/providers/inline/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ inline/
1414
tool_runtime/ # Tool runtime (RAG context retrieval)
1515
files/ # File storage and management
1616
file_processor/ # File processing (text extraction, etc.)
17+
skills/ # Skills API (versioned skill bundle management)
1718
__init__.py
1819
```
1920

@@ -29,3 +30,4 @@ Their factory function is typically named `get_provider_impl()` and returns an i
2930
- **`inference/sentence_transformers`** -- Runs embedding models using the sentence-transformers library.
3031
- **`inference/transformers`** -- Runs Llama models locally using the transformers library.
3132
- **`vector_io/sqlite_vec`** -- SQLite-based vector storage using the sqlite-vec extension.
33+
- **`skills/builtin`** -- Manages versioned skill bundles (zip archives with SKILL.md manifests). Stores bundles via the Files API and metadata in KVStore.

src/ogx/providers/inline/file_processor/docling/docling.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
# This source code is licensed under the terms described in the LICENSE file in
55
# the root directory of this source tree.
66

7+
import asyncio
78
import os
89
import tempfile
10+
import threading
911
import time
1012
import uuid
1113
from typing import Any
@@ -49,6 +51,7 @@ def __init__(self, config: DoclingFileProcessorConfig, files_api=None) -> None:
4951
self.converter = DocumentConverter(
5052
format_options={InputFormat.PDF: PdfFormatOption(pipeline_options=pipeline_options)}
5153
)
54+
self._converter_lock = threading.Lock()
5255

5356
async def process_file(
5457
self,
@@ -80,13 +83,25 @@ async def process_file(
8083
)
8184
content = content_response.body
8285

86+
return await asyncio.to_thread(self._process_content, content, filename, file_id, chunking_strategy, start_time)
87+
88+
def _process_content(
89+
self,
90+
content: bytes,
91+
filename: str,
92+
file_id: str | None,
93+
chunking_strategy: VectorStoreChunkingStrategy | None,
94+
start_time: float,
95+
) -> ProcessFileResponse:
96+
"""Convert and chunk file content. Runs in a thread."""
8397
# Preserve original file extension so DocumentConverter can detect the format
8498
suffix = os.path.splitext(filename)[1] or ".bin"
8599
with tempfile.NamedTemporaryFile(suffix=suffix, delete=True) as tmp:
86100
tmp.write(content)
87101
tmp.flush()
88102

89-
result = self.converter.convert(tmp.name)
103+
with self._converter_lock:
104+
result = self.converter.convert(tmp.name)
90105

91106
doc = result.document
92107
page_count = doc.num_pages()

src/ogx/providers/inline/file_processor/markitdown/markitdown_processor.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
# This source code is licensed under the terms described in the LICENSE file in
55
# the root directory of this source tree.
66

7+
import asyncio
78
import os
89
import tempfile
10+
import threading
911
import time
1012
import uuid
1113
from typing import Any
@@ -40,6 +42,7 @@ def __init__(self, config: MarkItDownFileProcessorConfig, files_api) -> None:
4042
self.config = config
4143
self.files_api = files_api
4244
self.converter = MarkItDown()
45+
self._converter_lock = threading.Lock()
4346

4447
async def process_file(
4548
self,
@@ -69,13 +72,25 @@ async def process_file(
6972
)
7073
content = content_response.body
7174

75+
return await asyncio.to_thread(self._process_content, content, filename, file_id, chunking_strategy, start_time)
76+
77+
def _process_content(
78+
self,
79+
content: bytes,
80+
filename: str,
81+
file_id: str | None,
82+
chunking_strategy: VectorStoreChunkingStrategy | None,
83+
start_time: float,
84+
) -> ProcessFileResponse:
85+
"""Convert and chunk file content. Runs in a thread."""
7286
suffix = os.path.splitext(filename)[1] or ".bin"
7387
with tempfile.NamedTemporaryFile(suffix=suffix, delete=True) as tmp:
7488
tmp.write(content)
7589
tmp.flush()
7690

7791
try:
78-
result = self.converter.convert(tmp.name)
92+
with self._converter_lock:
93+
result = self.converter.convert(tmp.name)
7994
except Exception as e:
8095
raise HTTPException(
8196
status_code=422,

src/ogx/providers/inline/file_processor/pypdf/pypdf.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# This source code is licensed under the terms described in the LICENSE file in
55
# the root directory of this source tree.
66

7+
import asyncio
78
import io
89
import mimetypes
910
import time
@@ -74,9 +75,11 @@ async def process_file(
7475
mime_category = mime_type.split("/")[0] if (mime_type and "/" in mime_type) else None
7576

7677
if mime_type == "application/pdf":
77-
return self._process_pdf(content, filename, file_id, chunking_strategy, start_time)
78+
return await asyncio.to_thread(self._process_pdf, content, filename, file_id, chunking_strategy, start_time)
7879
elif mime_category == "text":
79-
return self._process_text(content, filename, file_id, chunking_strategy, start_time)
80+
return await asyncio.to_thread(
81+
self._process_text, content, filename, file_id, chunking_strategy, start_time
82+
)
8083
else:
8184
raise HTTPException(
8285
status_code=422,

src/ogx/providers/inline/interactions/impl.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,6 @@ async def _passthrough_request(
298298
logger.error(
299299
"Passthrough request failed",
300300
status_code=resp.status_code,
301-
response_body=resp.text[:500],
302301
url=url,
303302
)
304303
return JSONResponse(content=resp.json(), status_code=resp.status_code)

0 commit comments

Comments
 (0)