Skip to content

Commit 40e5583

Browse files
Migrate from OAKRunner to ArazzoRunner (#42)
1 parent 594137f commit 40e5583

10 files changed

Lines changed: 117 additions & 101 deletions

File tree

mcp/pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,14 @@ mcp = "mcp.main:app"
5858

5959
[tool.black]
6060
line-length = 100
61-
target-version = ["py310"]
61+
target-version = ["py311"]
6262

6363
[tool.isort]
6464
profile = "black"
6565
line_length = 100
6666

6767
[tool.mypy]
68-
python_version = "3.10"
68+
python_version = "3.11"
6969
disallow_untyped_defs = true
7070
disallow_incomplete_defs = true
7171
check_untyped_defs = true
@@ -78,7 +78,7 @@ warn_unused_ignores = true
7878

7979
[tool.ruff]
8080
line-length = 100
81-
target-version = "py310"
81+
target-version = "py311"
8282
select = ["E", "F", "B", "W", "I", "N", "UP", "YTT", "S"]
8383
ignore = []
8484

python/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Jentic SDK is a comprehensive library for discovery and execution of APIs and workflows.
44

5-
The Jentic SDK is backed by the data in the [Open Agentic Knowledge (OAK)](https://github.com/jentic/oak) repository.
5+
The Jentic SDK is backed by the data in the [Jentic Public API](https://github.com/jentic/jentic-public-api) repository.
66

77
## Core API & Use Cases
88

python/pdm.lock

Lines changed: 17 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

python/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ dependencies = [
1212
"pyyaml>=6.0",
1313
"jsonpath-ng>=1.5.0",
1414
"httpx>=0.28.1",
15-
"oak-runner>=0.8.9"
15+
"arazzo_runner>=0.8.14"
1616
]
1717
requires-python = ">=3.11"
1818
readme = "README.md"

python/src/jentic/agent_runtime/config.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
from pathlib import Path
77
from typing import Any, Dict, List, Optional, Tuple
88

9-
from oak_runner.auth.auth_processor import AuthProcessor
10-
from oak_runner.auth.models import SecurityOption
11-
from oak_runner.extractor.openapi_extractor import extract_operation_io
12-
from oak_runner import OAKRunner
9+
from arazzo_runner.auth.auth_processor import AuthProcessor
10+
from arazzo_runner.auth.models import SecurityOption
11+
from arazzo_runner.extractor.openapi_extractor import extract_operation_io
12+
from arazzo_runner import ArazzoRunner
1313

1414
from jentic.api.api_hub import JenticAPIClient
1515
from jentic.models import GetFilesResponse
@@ -186,7 +186,7 @@ async def generate_config_from_uuids(
186186
)
187187

188188
# Step 5: Process authentication requirements
189-
env_mappings = OAKRunner.generate_env_mappings(arazzo_docs=all_arazzo_specs, source_descriptions=all_openapi_specs)
189+
env_mappings = ArazzoRunner.generate_env_mappings(arazzo_docs=all_arazzo_specs, source_descriptions=all_openapi_specs)
190190

191191
# Step 6: Compose final config
192192
final_config = {

python/src/jentic/agent_runtime/tool_execution.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from dataclasses import dataclass
66
from typing import Any, Dict, Optional
77

8-
from oak_runner import OAKRunner, WorkflowExecutionResult, WorkflowExecutionStatus
8+
from arazzo_runner import ArazzoRunner, WorkflowExecutionResult, WorkflowExecutionStatus
99

1010
from jentic import api
1111
from jentic.api import JenticAPIClient
@@ -54,7 +54,7 @@ def __init__(self, api_hub_client: Optional[JenticAPIClient] = None):
5454
self.api_hub_client = api_hub_client or JenticAPIClient()
5555

5656
async def execute_workflow(self, workflow_uuid: str, inputs: Dict[str, Any]) -> WorkflowResult:
57-
"""Executes a specified workflow using OAK runner.
57+
"""Executes a specified workflow using Arazzo runner.
5858
5959
Args:
6060
workflow_uuid: The UUID of the workflow to execute.
@@ -94,18 +94,18 @@ async def execute_workflow(self, workflow_uuid: str, inputs: Dict[str, Any]) ->
9494
error=f"Arazzo document or internal workflow ID missing for {workflow_uuid}",
9595
)
9696

97-
# 4. Instantiate OAKRunner
97+
# 4. Instantiate ArazzoRunner
9898
logger.debug(
99-
f"Instantiating OAKRunner for internal workflow ID: {friendly_workflow_id}"
99+
f"Instantiating ArazzoRunner for internal workflow ID: {friendly_workflow_id}"
100100
)
101-
runner = OAKRunner(
101+
runner = ArazzoRunner(
102102
arazzo_doc=arazzo_doc,
103103
source_descriptions=source_descriptions,
104104
)
105105

106106
# 5. Execute the workflow using the INTERNAL workflow ID
107107
logger.debug(
108-
f"Running workflow {friendly_workflow_id} via OAKRunner with UUID {workflow_uuid}."
108+
f"Running workflow {friendly_workflow_id} via ArazzoRunner with UUID {workflow_uuid}."
109109
)
110110
# Removed await as runner.execute_workflow seems synchronous based on TypeError
111111
execution_output: WorkflowExecutionResult = runner.execute_workflow(
@@ -136,7 +136,7 @@ async def execute_operation(
136136
inputs: Dict[str, Any],
137137
) -> OperationResult:
138138
"""
139-
Executes a specified API operation using OAKRunner after fetching required files from the API.
139+
Executes a specified API operation using ArazzoRunner after fetching required files from the API.
140140
141141
Args:
142142
operation_uuid: The UUID of the operation to execute.
@@ -167,7 +167,7 @@ async def execute_operation(
167167
)
168168
operation_entry = exec_files_response.operations[operation_uuid]
169169

170-
# Prepare OpenAPI spec for OAKRunner
170+
# Prepare OpenAPI spec for ArazzoRunner
171171
openapi_content = None
172172
openapi_files = exec_files_response.files.get("open_api", {})
173173
if operation_entry.files.open_api:
@@ -183,15 +183,15 @@ async def execute_operation(
183183
)
184184
source_descriptions = {"default": openapi_content}
185185

186-
# Prepare OAKRunner and execute the operation
187-
runner = OAKRunner(source_descriptions=source_descriptions)
186+
# Prepare ArazzoRunner and execute the operation
187+
runner = ArazzoRunner(source_descriptions=source_descriptions)
188188
# Pass operation_uuid, path, and method from the operation_entry
189-
oak_result: Any = runner.execute_operation(
189+
runner_result: Any = runner.execute_operation(
190190
inputs=inputs, operation_path=f"{operation_entry.method} {operation_entry.path}"
191191
)
192-
logger.debug(f"Operation execution result: {oak_result}")
192+
logger.debug(f"Operation execution result: {runner_result}")
193193

194-
return self._process_operation_result(oak_result, operation_uuid, inputs)
194+
return self._process_operation_result(runner_result, operation_uuid, inputs)
195195
except Exception as e:
196196
logger.exception(f"Error executing operation {operation_uuid}: {e}")
197197
return OperationResult(
@@ -201,18 +201,18 @@ async def execute_operation(
201201
)
202202

203203
def _process_operation_result(
204-
self, oak_result: Dict[str, Any], operation_uuid: str, inputs: Dict[str, Any]
204+
self, runner_result: Dict[str, Any], operation_uuid: str, inputs: Dict[str, Any]
205205
) -> "OperationResult":
206-
"""Process the OAKRunner operation result, check status codes, and handle casting.
206+
"""Process the ArazzoRunner operation result, check status codes, and handle casting.
207207
208208
Args:
209-
oak_result: The result dictionary from OAKRunner.execute_operation.
209+
runner_result: The result dictionary from ArazzoRunner.execute_operation.
210210
operation_uuid: The UUID of the operation being executed, for logging.
211211
212212
Returns:
213213
An OperationResult object.
214214
"""
215-
status_code_uncast = oak_result.get("status_code")
215+
status_code_uncast = runner_result.get("status_code")
216216

217217
if status_code_uncast is None:
218218
logger.debug(
@@ -221,7 +221,7 @@ def _process_operation_result(
221221
)
222222
return OperationResult(
223223
success=True,
224-
output=oak_result.get("body") if "body" in oak_result else oak_result,
224+
output=runner_result.get("body") if "body" in runner_result else runner_result,
225225
inputs=inputs,
226226
)
227227

@@ -240,7 +240,7 @@ def _process_operation_result(
240240
return OperationResult(
241241
success=False,
242242
error=f"Invalid status_code format: '{status_code_uncast}'. Expected an integer or integer-convertible value.",
243-
output=oak_result, # Include full OAK result for context on casting errors
243+
output=runner_result, # Include full OAK result for context on casting errors
244244
inputs=inputs,
245245
)
246246
else:
@@ -252,12 +252,12 @@ def _process_operation_result(
252252
return OperationResult(
253253
success=True,
254254
status_code=status_code,
255-
output=oak_result.get("body") if "body" in oak_result else oak_result,
255+
output=runner_result.get("body") if "body" in runner_result else runner_result,
256256
inputs=inputs,
257257
)
258258
else:
259259
# Non-2xx status code, indicates an error
260-
body_content = oak_result.get("body")
260+
body_content = runner_result.get("body")
261261
error_detail = ""
262262

263263
if isinstance(body_content, dict):
@@ -284,7 +284,7 @@ def _process_operation_result(
284284
success=False,
285285
status_code=status_code,
286286
error=error_detail,
287-
output=oak_result, # Return the full OAK result as output for context on errors
287+
output=runner_result, # Return the full OAK result as output for context on errors
288288
inputs=inputs,
289289
)
290290

python/src/jentic/api/api_hub.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -133,17 +133,17 @@ def _build_source_descriptions(
133133
openapi_file_id = openapi_file_id_obj.id
134134
if openapi_file_id in all_openapi_files:
135135
file_entry = all_openapi_files[openapi_file_id]
136-
# Store content and oak_path for direct matching
137-
# Assumes file_entry has an 'oak_path' attribute from the API response
138-
if hasattr(file_entry, 'oak_path') and file_entry.oak_path is not None:
136+
# Store content and source_path for direct matching
137+
# Assumes file_entry has a 'source_path' attribute from the API response
138+
if hasattr(file_entry, 'source_path') and file_entry.source_path is not None:
139139
openapi_files[openapi_file_id] = {
140140
"content": file_entry.content,
141-
"oak_path": file_entry.oak_path
141+
"source_path": file_entry.source_path
142142
}
143-
logger.debug(f"Found OpenAPI file with oak_path: {file_entry.oak_path} (ID: {openapi_file_id})")
143+
logger.debug(f"Found OpenAPI file with source_path: {file_entry.source_path} (ID: {openapi_file_id})")
144144
else:
145145
logger.warning(
146-
f"OpenAPI file entry with ID {openapi_file_id} (filename: {file_entry.filename}) is missing 'oak_path'. Cannot use for matching."
146+
f"OpenAPI file entry with ID {openapi_file_id} (filename: {file_entry.filename}) is missing 'source_path'. Cannot use for matching."
147147
)
148148
else:
149149
logger.warning(
@@ -152,7 +152,7 @@ def _build_source_descriptions(
152152

153153
if not openapi_files:
154154
logger.warning(
155-
f"No usable OpenAPI file content (with oak_path) found for workflow {workflow_entry.workflow_id} despite references."
155+
f"No usable OpenAPI file content (with source_path) found for workflow {workflow_entry.workflow_id} despite references."
156156
)
157157
elif not all_openapi_files:
158158
logger.warning(
@@ -163,7 +163,7 @@ def _build_source_descriptions(
163163
f"Workflow {workflow_entry.workflow_id} does not reference any OpenAPI files."
164164
)
165165

166-
# 3. Map each Arazzo source description to matching OpenAPI content by oak_path
166+
# 3. Map each Arazzo source description to matching OpenAPI content by source_path
167167
if arazzo_source_names and openapi_files:
168168
# Extract source descriptions with their URLs
169169
arazzo_sources_with_urls = []
@@ -178,33 +178,33 @@ def _build_source_descriptions(
178178
except Exception as e:
179179
logger.error(f"Error extracting URLs from sourceDescriptions: {e}")
180180

181-
# Match Arazzo sourceDescriptions to OpenAPI files by comparing source.url with file.oak_path
181+
# Match Arazzo sourceDescriptions to OpenAPI files by comparing source.url with file.source_path
182182
for source in arazzo_sources_with_urls:
183183
source_name = source["name"]
184184
source_url = source["url"]
185185

186186
matched = False
187187
for file_id, file_info in openapi_files.items():
188-
openapi_oak_path = file_info["oak_path"]
188+
openapi_source_path = file_info["source_path"]
189189

190-
if source_url == openapi_oak_path:
190+
if source_url == openapi_source_path:
191191
source_descriptions[source_name] = file_info["content"]
192192
matched = True
193193
logger.info(
194194
f"Matched Arazzo source '{source_name}' (URL: {source_url}) "
195-
f"to OpenAPI file with oak_path '{openapi_oak_path}' (ID: {file_id})"
195+
f"to OpenAPI file with source_path '{openapi_source_path}' (ID: {file_id})"
196196
)
197197
break # Found the match for this Arazzo source
198198

199199
if not matched:
200200
logger.warning(
201-
f"Could not find an OpenAPI file with oak_path matching Arazzo sourceDescription URL '{source_url}' "
201+
f"Could not find an OpenAPI file with source_path matching Arazzo sourceDescription URL '{source_url}' "
202202
f"for source name '{source_name}' in workflow {workflow_entry.workflow_id}. This source will not be available."
203203
)
204204

205205
elif not openapi_files and arazzo_source_names:
206206
logger.warning(
207-
f"No OpenAPI files with oak_path were available to match against Arazzo source descriptions for workflow {workflow_entry.workflow_id}."
207+
f"No OpenAPI files with source_path were available to match against Arazzo source descriptions for workflow {workflow_entry.workflow_id}."
208208
)
209209

210210
if not source_descriptions and arazzo_source_names:

python/src/jentic/models.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Any, Dict, List, Optional
22

3-
from pydantic import BaseModel, Field
3+
from pydantic import BaseModel, Field, field_validator, model_validator
44

55

66
# Represents a reference to a file ID
@@ -14,7 +14,25 @@ class FileEntry(BaseModel):
1414
filename: str
1515
type: str
1616
content: Dict[str, Any] # Content can be any valid JSON object
17-
oak_path: Optional[str] = None # Contextual path for the file, e.g., from Arazzo spec_files
17+
source_path: Optional[str] = None # Contextual path for the file, e.g., from Arazzo spec_files
18+
19+
@model_validator(mode='before')
20+
@classmethod
21+
def handle_oak_path_alias(cls, values):
22+
"""Handle backward compatibility for oak_path field name."""
23+
if isinstance(values, dict):
24+
# If oak_path is provided but source_path is not, use oak_path
25+
if 'oak_path' in values and 'source_path' not in values:
26+
values['source_path'] = values.pop('oak_path')
27+
# If both are provided, prefer source_path and remove oak_path
28+
elif 'oak_path' in values and 'source_path' in values:
29+
values.pop('oak_path')
30+
return values
31+
32+
@property
33+
def oak_path(self) -> Optional[str]:
34+
"""Backward compatibility property for oak_path."""
35+
return self.source_path
1836

1937

2038
# Represents an API reference within a workflow

0 commit comments

Comments
 (0)