96 lines
3.4 KiB
Python
96 lines
3.4 KiB
Python
|
|
import json
|
||
|
|
import urllib.request
|
||
|
|
import urllib.error
|
||
|
|
import logging
|
||
|
|
from pr.config import DEFAULT_TEMPERATURE, DEFAULT_MAX_TOKENS
|
||
|
|
from pr.core.context import auto_slim_messages
|
||
|
|
|
||
|
|
logger = logging.getLogger('pr')
|
||
|
|
|
||
|
|
def call_api(messages, model, api_url, api_key, use_tools, tools_definition, verbose=False):
|
||
|
|
try:
|
||
|
|
messages = auto_slim_messages(messages, verbose=verbose)
|
||
|
|
|
||
|
|
logger.debug(f"=== API CALL START ===")
|
||
|
|
logger.debug(f"Model: {model}")
|
||
|
|
logger.debug(f"API URL: {api_url}")
|
||
|
|
logger.debug(f"Use tools: {use_tools}")
|
||
|
|
logger.debug(f"Message count: {len(messages)}")
|
||
|
|
headers = {
|
||
|
|
'Content-Type': 'application/json',
|
||
|
|
}
|
||
|
|
|
||
|
|
if api_key:
|
||
|
|
headers['Authorization'] = f'Bearer {api_key}'
|
||
|
|
|
||
|
|
data = {
|
||
|
|
'model': model,
|
||
|
|
'messages': messages,
|
||
|
|
'temperature': DEFAULT_TEMPERATURE,
|
||
|
|
'max_tokens': DEFAULT_MAX_TOKENS
|
||
|
|
}
|
||
|
|
|
||
|
|
if "gpt-5" in model:
|
||
|
|
del data['temperature']
|
||
|
|
del data['max_tokens']
|
||
|
|
logger.debug("GPT-5 detected: removed temperature and max_tokens")
|
||
|
|
|
||
|
|
if use_tools:
|
||
|
|
data['tools'] = tools_definition
|
||
|
|
data['tool_choice'] = 'auto'
|
||
|
|
logger.debug(f"Tool calling enabled with {len(tools_definition)} tools")
|
||
|
|
|
||
|
|
request_json = json.dumps(data)
|
||
|
|
logger.debug(f"Request payload size: {len(request_json)} bytes")
|
||
|
|
|
||
|
|
req = urllib.request.Request(
|
||
|
|
api_url,
|
||
|
|
data=request_json.encode('utf-8'),
|
||
|
|
headers=headers,
|
||
|
|
method='POST'
|
||
|
|
)
|
||
|
|
|
||
|
|
logger.debug("Sending HTTP request...")
|
||
|
|
with urllib.request.urlopen(req) as response:
|
||
|
|
response_data = response.read().decode('utf-8')
|
||
|
|
logger.debug(f"Response received: {len(response_data)} bytes")
|
||
|
|
result = json.loads(response_data)
|
||
|
|
|
||
|
|
if 'usage' in result:
|
||
|
|
logger.debug(f"Token usage: {result['usage']}")
|
||
|
|
if 'choices' in result and result['choices']:
|
||
|
|
choice = result['choices'][0]
|
||
|
|
if 'message' in choice:
|
||
|
|
msg = choice['message']
|
||
|
|
logger.debug(f"Response role: {msg.get('role', 'N/A')}")
|
||
|
|
if 'content' in msg and msg['content']:
|
||
|
|
logger.debug(f"Response content length: {len(msg['content'])} chars")
|
||
|
|
if 'tool_calls' in msg:
|
||
|
|
logger.debug(f"Response contains {len(msg['tool_calls'])} tool call(s)")
|
||
|
|
|
||
|
|
logger.debug("=== API CALL END ===")
|
||
|
|
return result
|
||
|
|
|
||
|
|
except urllib.error.HTTPError as e:
|
||
|
|
error_body = e.read().decode('utf-8')
|
||
|
|
logger.error(f"API HTTP Error: {e.code} - {error_body}")
|
||
|
|
logger.debug("=== API CALL FAILED ===")
|
||
|
|
return {"error": f"API Error: {e.code}", "message": error_body}
|
||
|
|
except Exception as e:
|
||
|
|
logger.error(f"API call failed: {e}")
|
||
|
|
logger.debug("=== API CALL FAILED ===")
|
||
|
|
return {"error": str(e)}
|
||
|
|
|
||
|
|
def list_models(model_list_url, api_key):
|
||
|
|
try:
|
||
|
|
req = urllib.request.Request(model_list_url)
|
||
|
|
if api_key:
|
||
|
|
req.add_header('Authorization', f'Bearer {api_key}')
|
||
|
|
|
||
|
|
with urllib.request.urlopen(req) as response:
|
||
|
|
data = json.loads(response.read().decode('utf-8'))
|
||
|
|
|
||
|
|
return data.get('data', [])
|
||
|
|
except Exception as e:
|
||
|
|
return {"error": str(e)}
|