mirror of
https://github.com/tiennm99/litellm.git
synced 2026-06-17 18:48:36 +00:00
f11c12d157
This reverts the Bedrock CI account migration (#28728). The original account (888602223428) was put under an AWS security restriction after a leaked key and has since been reactivated, while the replacement account (941277531214) lacks access to several models the suites exercise (legacy Bedrock Claude 3 models, Cohere, Nova Canvas image gen, Bedrock batch inference, and flagship Opus). Pointing CI back at the reactivated account restores that coverage. This is the exact inverse of #28728: all hardcoded 941277531214 references go back to 888602223428 (provisioned/imported-model ARNs, AgentCore runtime ARNs and their suffixes, batch execution role ARN, and the example proxy config), the S3 buckets revert to litellm-proxy and load-testing-oct, the guardrail IDs revert to wf0hkdb5x07f and ff6ujrregl1q, the SageMaker endpoint and Knowledge Base revert to their original ids, and the live-call tests go back to the legacy model strings. The grid_spec fail_reason workaround for the unentitled Opus cells is dropped while keeping the unrelated bedrock_effort_ceiling field added after the migration. The CircleCI AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY env vars still point at 941277531214 and must be set to the reactivated account's fresh credentials separately via the CircleCI API; AWS_REGION_NAME stays us-west-2.
151 lines
4.5 KiB
Python
151 lines
4.5 KiB
Python
# What is this?
|
|
## Test to make sure function call response always works with json.loads() -> no extra parsing required. Relevant issue - https://github.com/BerriAI/litellm/issues/2654
|
|
import os
|
|
import sys
|
|
import traceback
|
|
|
|
from dotenv import load_dotenv
|
|
|
|
load_dotenv()
|
|
import io
|
|
import os
|
|
|
|
sys.path.insert(
|
|
0, os.path.abspath("../..")
|
|
) # Adds the parent directory to the system path
|
|
import json
|
|
import warnings
|
|
from typing import List
|
|
|
|
import pytest
|
|
|
|
import litellm
|
|
from litellm import completion
|
|
|
|
|
|
# Just a stub to keep the sample code simple
|
|
class Trade:
|
|
def __init__(self, order: dict):
|
|
self.order = order
|
|
|
|
@staticmethod
|
|
def buy(order: dict):
|
|
return Trade(order)
|
|
|
|
@staticmethod
|
|
def sell(order: dict):
|
|
return Trade(order)
|
|
|
|
|
|
def trade(model_name: str) -> List[Trade]: # type: ignore
|
|
def parse_order(order: dict) -> Trade:
|
|
action = order["action"]
|
|
|
|
if action == "buy":
|
|
return Trade.buy(order)
|
|
elif action == "sell":
|
|
return Trade.sell(order)
|
|
else:
|
|
raise ValueError(f"Invalid action {action}")
|
|
|
|
def parse_call(call) -> List[Trade]:
|
|
arguments = json.loads(call.function.arguments)
|
|
|
|
trades = [parse_order(order) for order in arguments["orders"]]
|
|
return trades
|
|
|
|
tool_spec = {
|
|
"type": "function",
|
|
"function": {
|
|
"name": "trade",
|
|
"description": "Execute orders to manage the portfolio. Orders will be executed immediately at the stated prices.",
|
|
"parameters": {
|
|
"type": "object",
|
|
"properties": {
|
|
"orders": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"properties": {
|
|
"action": {"type": "string", "enum": ["buy", "sell"]},
|
|
"asset": {"type": "string"},
|
|
"amount": {
|
|
"type": "number",
|
|
"description": "Amount of asset to buy or sell.",
|
|
},
|
|
},
|
|
"required": ["action", "asset", "amount"],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
try:
|
|
response = completion(
|
|
model_name,
|
|
[
|
|
{
|
|
"role": "system",
|
|
"content": """You are an expert asset manager, managing a portfolio.
|
|
|
|
Always use the `trade` function. Make sure that you call it correctly. For example, the following is a valid call:
|
|
```
|
|
trade({
|
|
"orders": [
|
|
{"action": "buy", "asset": "BTC", "amount": 0.1},
|
|
{"action": "sell", "asset": "ETH", "amount": 0.2}
|
|
]
|
|
})
|
|
```
|
|
|
|
If there are no trades to make, call `trade` with an empty array:
|
|
```
|
|
trade({ "orders": [] })
|
|
```
|
|
""",
|
|
},
|
|
{
|
|
"role": "user",
|
|
"content": """Manage the portfolio.
|
|
|
|
Don't jabber.
|
|
|
|
This is the current market data:
|
|
```
|
|
{market_data}
|
|
```
|
|
|
|
Your portfolio is as follows:
|
|
```
|
|
{portfolio}
|
|
```
|
|
""".replace(
|
|
"{market_data}", "BTC: 64,000 USD\nETH: 3,500 USD"
|
|
).replace(
|
|
"{portfolio}", "USD: 1000, BTC: 0.1, ETH: 0.2"
|
|
),
|
|
},
|
|
],
|
|
tools=[tool_spec],
|
|
tool_choice={
|
|
"type": "function",
|
|
"function": {"name": tool_spec["function"]["name"]}, # type: ignore
|
|
},
|
|
)
|
|
calls = response.choices[0].message.tool_calls
|
|
trades = [trade for call in calls for trade in parse_call(call)]
|
|
return trades
|
|
except litellm.InternalServerError:
|
|
pass
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"model", ["claude-haiku-4-5-20251001", "anthropic.claude-3-haiku-20240307-v1:0"]
|
|
)
|
|
@pytest.mark.flaky(retries=6, delay=10)
|
|
def test_function_call_parsing(model):
|
|
trades = trade(model)
|
|
print([trade.order for trade in trades if trade is not None])
|