Skip to content

Commit 1c9bc0f

Browse files
authored
Add response-api support
Add response-api support
2 parents d3f9795 + 3aee202 commit 1c9bc0f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1941
-138
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,4 @@ cython_debug/
8080
.pypirc
8181

8282
# Demo folder
83-
.demo
83+
.demo

Makefile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Define the directory containing the source code
22
SRC_DIR := ./src
33
TEST_DIR := ./tests
4-
4+
EXAMPLE_DIR := ./examples
55

66
.PHONY: all
77
all: test lint build
@@ -24,21 +24,21 @@ dev: ## Install development dependencies.
2424

2525
.PHONY: test
2626
test: ## Run tests.
27-
uv run --no-reinstall pytest $(TEST_DIR) --cov --cov-config=.coveragerc -vv -s
27+
uv run --no-project --no-reinstall pytest $(TEST_DIR) --cov --cov-config=.coveragerc -vv -s
2828

2929
.PHONY: clean
3030
clean: ## Remove build artifacts.
3131
rm -rf build dist *.egg-info .pytest_cache .coverage
3232

3333
.PHONY: format
3434
format: ## Format code using ruff.
35-
uv run --no-reinstall isort $(SRC_DIR) $(TEST_DIR)
36-
uv run --no-reinstall ruff format $(SRC_DIR) $(TEST_DIR); uv run --no-reinstall ruff check --fix $(SRC_DIR) $(TEST_DIR)
35+
uv run --no-project --no-reinstall isort $(SRC_DIR) $(TEST_DIR) $(EXAMPLE_DIR)
36+
uv run --no-project --no-reinstall ruff format $(SRC_DIR) $(TEST_DIR) $(EXAMPLE_DIR); uv run --no-project --no-reinstall ruff check --fix $(SRC_DIR) $(TEST_DIR) $(EXAMPLE_DIR)
3737

3838
.PHONY: lint
3939
lint: ## Run linters using ruff.
40-
uv run --no-reinstall ruff format --diff $(SRC_DIR) $(TEST_DIR)
41-
uv run --no-reinstall mypy $(SRC_DIR)
40+
uv run --no-project --no-reinstall ruff format --diff $(SRC_DIR) $(TEST_DIR)
41+
uv run --no-project --no-reinstall mypy $(SRC_DIR) $(TEST_DIR)
4242

4343
.PHONY: check
4444
check: format lint ## Run format and lint.

README.md

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,7 @@
33
[![PyPI - Version](https://img.shields.io/pypi/v/oci-openai.svg)](https://pypi.org/project/oci-openai)
44
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/oci-openai.svg)](https://pypi.org/project/oci-openai)
55

6-
**OCI-OpenAI** is a client library jointly maintained by the **Oracle Cloud Infrastructure (OCI) Generative AI** and **OCI Data Science** teams.
7-
8-
This package simplifies integration between **OpenAI’s Python SDK** and Oracle Cloud Infrastructure services — supporting both the **OCI Generative AI Service** and the **OCI Data Science Model Deployment** service.
9-
It provides robust authentication and authorization utilities that allow developers to securely connect to and invoke OCI-hosted large language models (LLMs) using standard OpenAI-compatible APIs.
10-
11-
By leveraging this library, you can:
12-
- Seamlessly connect to **OCI Generative AI** endpoints.
13-
- Interact with **OCI Data Science Model Deployment** LLM endpoints using the same OpenAI-style interface.
14-
- Ensure compliance with OCI security and access control best practices.
6+
The **OCI OpenAI** Python library provides secure and convenient access to the OpenAI-compatible REST API hosted by **OCI Generative AI Service** and **OCI Data Science Model Deployment** Service.
157

168
---
179

@@ -78,8 +70,8 @@ print(completion.model_dump_json())
7870
from oci_openai import AsyncOciOpenAI, OciSessionAuth
7971

8072
client = AsyncOciOpenAI(
81-
service_endpoint="https://inference.generativeai.us-chicago-1.oci.oraclecloud.com",
8273
auth=OciSessionAuth(profile_name="<profile name>"),
74+
region="us-chicago-1",
8375
compartment_id="<compartment ocid>",
8476
)
8577

@@ -106,10 +98,10 @@ from oci_openai import OciUserPrincipalAuth
10698
# Example for OCI Generative AI endpoint
10799
client = OpenAI(
108100
api_key="OCI",
109-
base_url="https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/20231130/actions/v1",
101+
base_url="https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/openai/v1",
110102
http_client=httpx.Client(
111-
auth=OciUserPrincipalAuth(profile_name="DEFAULT"),
112-
headers={"CompartmentId": COMPARTMENT_ID}
103+
auth=OciUserPrincipalAuth(profile_name="DEFAULT"),
104+
headers={"opc-compartment-id": COMPARTMENT_ID}
113105
),
114106
)
115107

@@ -139,9 +131,9 @@ COMPARTMENT_ID=os.getenv("OCI_COMPARTMENT_ID", "<compartment_id>")
139131
llm = ChatOpenAI(
140132
model="<model-name>", # for example "xai.grok-4-fast-reasoning"
141133
api_key="OCI",
142-
base_url="https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/20231130/actions/v1",
134+
base_url="https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/openai/v1",
143135
http_client=httpx.Client(
144-
auth=OciUserPrincipalAuth(profile_name="DEFAULT"),
136+
auth=OciUserPrincipalAuth(profile_name="DEFAULT"),
145137
headers={"CompartmentId": COMPARTMENT_ID}
146138
),
147139
# use_responses_api=True
@@ -175,7 +167,7 @@ print(ai_msg)
175167
from oci_openai import OciOpenAI, OciSessionAuth
176168

177169
client = OciOpenAI(
178-
service_endpoint="https://modeldeployment.us-ashburn-1.oci.customer-oci.com/<OCID>/predict/v1",
170+
base_url="https://modeldeployment.us-ashburn-1.oci.customer-oci.com/<OCID>/predict/v1",
179171
auth=OciSessionAuth(profile_name="<profile name>")
180172
)
181173

@@ -199,7 +191,7 @@ from oci_openai import AsyncOciOpenAI, OciSessionAuth
199191

200192
# Example for OCI Data Science Model Deployment endpoint
201193
client = AsyncOciOpenAI(
202-
service_endpoint="https://modeldeployment.us-ashburn-1.oci.customer-oci.com/<OCID>/predict/v1",
194+
base_url="https://modeldeployment.us-ashburn-1.oci.customer-oci.com/<OCID>/predict/v1",
203195
auth=OciSessionAuth(profile_name="<profile name>")
204196
)
205197

examples/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Copyright (c) 2025 Oracle and/or its affiliates.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/

examples/common.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Copyright (c) 2025 Oracle and/or its affiliates.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
3+
4+
from oci_openai import OciOpenAI, OciSessionAuth
5+
6+
COMPARTMENT_ID = ""
7+
CONVERSATION_STORE_ID = ""
8+
OVERRIDE_URL = ""
9+
PROFILE_NAME = "oc1"
10+
REGION = ""
11+
12+
13+
oci_openai_client = OciOpenAI(
14+
auth=OciSessionAuth(profile_name=PROFILE_NAME),
15+
compartment_id=COMPARTMENT_ID,
16+
region=REGION,
17+
service_endpoint=OVERRIDE_URL,
18+
conversation_store_id=CONVERSATION_STORE_ID,
19+
)

examples/fc_tools.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Copyright (c) 2025 Oracle and/or its affiliates.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
3+
4+
from openai.types.responses.function_tool_param import FunctionToolParam
5+
6+
7+
def get_stock_price(symbol: str) -> dict:
8+
"""Get the current stock price for a given symbol."""
9+
print(f"Fetching stock price for {symbol}...")
10+
11+
# Mock stock data
12+
return {
13+
"symbol": symbol.upper(),
14+
"price": 175.42 if symbol.upper() == "AAPL" else 198.76,
15+
"currency": "USD",
16+
"change": 2.34 if symbol.upper() == "AAPL" else -1.23,
17+
"change_percent": 1.35 if symbol.upper() == "AAPL" else -0.62,
18+
"last_updated": "2025-06-26T15:00:00Z",
19+
}
20+
21+
22+
def get_current_weather(location: str, unit: str = "fahrenheit") -> dict:
23+
"""Get current weather for a given location.
24+
25+
Args:
26+
location: The city and state, e.g., San Francisco, CA
27+
unit: The unit of temperature (celsius or fahrenheit)
28+
29+
Returns:
30+
dict: Weather information
31+
"""
32+
# In a real application, you would call a weather API here
33+
# This is a mock implementation for demonstration
34+
print(f"Fetching weather for {location} in {unit}...")
35+
36+
# Mock weather data
37+
weather_data = {
38+
"location": location,
39+
"temperature": "72",
40+
"unit": "fahrenheit",
41+
"forecast": ["sunny", "windy"],
42+
"humidity": "65%",
43+
"description": "Sunny with a gentle breeze",
44+
}
45+
46+
return weather_data
47+
48+
49+
def recommend_clothing(temperature, unit="fahrenheit"):
50+
"""
51+
Returns clothing recommendations based on input temperature and unit.
52+
53+
Parameters:
54+
temperature (float or int): The temperature value to base the recommendation on.
55+
unit (str, optional): The unit of the temperature value.
56+
Can be 'fahrenheit' or 'celsius'. Defaults to 'fahrenheit'.
57+
"""
58+
# Convert to Fahrenheit if input is in Celsius
59+
if unit.lower() == "celsius":
60+
temperature = temperature * 9 / 5 + 32
61+
62+
if temperature >= 80:
63+
return "It's hot! Wear shorts and a t-shirt."
64+
elif temperature >= 65:
65+
return "It's warm. A short-sleeve shirt and pants are fine."
66+
elif temperature >= 50:
67+
return "It's a bit chilly. Wear a light jacket or sweater."
68+
elif temperature >= 32:
69+
return "It's cold. Wear a coat, sweater, and possibly a hat."
70+
else:
71+
return "It's freezing! Dress warmly in layers, including a winter coat, gloves, and a hat."
72+
73+
74+
fc_tools = [
75+
FunctionToolParam(
76+
name="get_current_weather",
77+
strict=True,
78+
parameters={
79+
"type": "object",
80+
"properties": {
81+
"location": {
82+
"type": "string",
83+
"description": "City and country e.g. Bogotá, Colombia",
84+
}
85+
},
86+
"required": ["location"],
87+
"additionalProperties": False,
88+
},
89+
type="function",
90+
description="Get current weather for a given location.",
91+
),
92+
FunctionToolParam(
93+
name="recommend_clothing",
94+
strict=True,
95+
parameters={
96+
"type": "object",
97+
"properties": {
98+
"temperature": {
99+
"type": "integer",
100+
"description": "The temperature value to base the recommendation on.",
101+
}
102+
},
103+
"required": ["temperature"],
104+
"additionalProperties": False,
105+
},
106+
type="function",
107+
description="Returns clothing recommendations based on input temperature and unit.",
108+
),
109+
]
110+
111+
112+
def execute_function_call(function_name, function_args):
113+
# Call the function
114+
if function_name == "get_current_weather":
115+
return get_current_weather(**function_args)
116+
elif function_name == "recommend_clothing":
117+
return recommend_clothing(**function_args)
118+
else:
119+
return None

examples/oci_openai/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Copyright (c) 2025 Oracle and/or its affiliates.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Copyright (c) 2025 Oracle and/or its affiliates.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
3+
4+
# mypy: ignore-errors
5+
6+
# OpenAI Agents SDK imports
7+
import asyncio
8+
9+
from agents import Agent, Runner, set_default_openai_client, trace
10+
11+
# OCI OpenAI Client SDK imports
12+
from oci_openai import AsyncOciOpenAI, OciSessionAuth
13+
14+
# set_default_openai_key(openai_apikey)
15+
COMPARTMENT_ID = ""
16+
CONVERSATION_STORE_ID = ""
17+
OVERRIDE_URL = ""
18+
REGION = ""
19+
PROFILE_NAME = "oc1"
20+
MODEL = "openai.gpt-4o"
21+
22+
23+
# Create OCI OpenAI Client passing auth and runtime configuration
24+
def get_oci_openai_client():
25+
return AsyncOciOpenAI(
26+
auth=OciSessionAuth(profile_name=PROFILE_NAME),
27+
compartment_id=COMPARTMENT_ID,
28+
region=REGION,
29+
service_endpoint=OVERRIDE_URL,
30+
conversation_store_id=CONVERSATION_STORE_ID,
31+
)
32+
33+
34+
# Set the OCI OpenAI Client as the default client to use with OpenAI Agents
35+
set_default_openai_client(get_oci_openai_client())
36+
37+
38+
async def main():
39+
agent = Agent(name="Assistant", instructions="You are a helpful assistant", model=MODEL)
40+
with trace("Trace workflow"):
41+
result = await Runner.run(agent, "Write a haiku about recursion in programming.")
42+
print(result.final_output)
43+
44+
45+
if __name__ == "__main__":
46+
asyncio.run(main())
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright (c) 2025 Oracle and/or its affiliates.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
3+
4+
from rich import print
5+
6+
from examples.common import oci_openai_client
7+
8+
items = oci_openai_client.conversations.items.create(
9+
"conv_977e8f9d624849a79b8eca5e6d67f69a",
10+
items=[
11+
{
12+
"type": "message",
13+
"role": "user",
14+
"content": [{"type": "input_text", "text": "Hello!"}],
15+
},
16+
{
17+
"type": "message",
18+
"role": "user",
19+
"content": [{"type": "input_text", "text": "How are you?"}],
20+
},
21+
],
22+
)
23+
print(items.data)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright (c) 2025 Oracle and/or its affiliates.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
3+
4+
from rich import print
5+
6+
from examples.common import oci_openai_client
7+
8+
conversation = oci_openai_client.conversations.items.delete(
9+
conversation_id="conv_977e8f9d624849a79b8eca5e6d67f69a",
10+
item_id="msg_f7cfcdcf47c944cebb414a495e3c3721",
11+
)
12+
print(conversation)

0 commit comments

Comments
 (0)