-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathllm_processor.py
More file actions
140 lines (112 loc) · 5.49 KB
/
llm_processor.py
File metadata and controls
140 lines (112 loc) · 5.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import requests
import configparser
import os
import json
from dotenv import load_dotenv
load_dotenv()
DEFAULT_MODEL = 'gpt-4o' # Azure OpenAI model name
DEFAULT_API_URL = 'https://{your-resource-name}.openai.azure.com/openai/deployments/{deployment-name}/chat/completions?api-version=2024-02-15-preview'
def load_api_config():
"""
Load API configuration from config.ini file
Returns:
dict: API configuration
"""
config = configparser.ConfigParser()
try:
config.read('config.ini')
api_config = {
'api_key': config.get('LLM_API', 'api_key', fallback=os.getenv('AZURE_OPENAI_API_KEY', '')),
'model': config.get('LLM_API', 'model', fallback=DEFAULT_MODEL),
'api_url': config.get('LLM_API', 'api_url', fallback=DEFAULT_API_URL),
'api_version': config.get('LLM_API', 'api_version', fallback='2024-02-15-preview')
}
return api_config
except Exception as e:
print(f"Error loading API config: {str(e)}")
# Default configuration
return {
'api_key': os.getenv('AZURE_OPENAI_API_KEY', ''),
'model': DEFAULT_MODEL,
'api_url': DEFAULT_API_URL,
'api_version': '2024-02-15-preview'
}
import re
def process_content_with_llm(content, system_prompt=None, heading=None):
"""Process content with Azure OpenAI API
Args:
content (str): The content to process
system_prompt (str, optional): Custom system prompt to guide the LLM
heading (str, optional): The section heading containing word limit info
Returns:
tuple: (processed_content, token_usage_dict)
where token_usage_dict contains:
{
'prompt_tokens': int, # Tokens in the input/prompt
'completion_tokens': int, # Tokens in the output/completion
'total_tokens': int # Total tokens used
}
"""
if not content.strip():
return content, {'prompt_tokens': 0, 'completion_tokens': 0, 'total_tokens': 0}
# Extract word limit from heading if present
word_limit = None
if heading:
limit_match = re.search(r'word limit:\s*(\d+)', heading, re.IGNORECASE)
if limit_match:
word_limit = int(limit_match.group(1))
api_config = load_api_config()
api_key = api_config['api_key']
model = api_config['model']
api_url = api_config['api_url']
if not api_key:
print("API key not configured. Using mock processing.")
return f"{content}\n[Note: Content would be processed with LLM if API key was configured]", {'prompt_tokens': 0, 'completion_tokens': 0, 'total_tokens': 0}
def create_payload(system_prompt, user_prompt, model, temperature=0.3):
return {
"messages": [
{"role": "system", "content": system_prompt if system_prompt else "You are a professional editor focusing on clarity and brevity."},
{"role": "user", "content": user_prompt}
],
"temperature": temperature,
"max_tokens": 2000
}
# Prepare the prompt for the LLM
prompt = f"Process this text. Output ONLY the edited content without any additional text:\n\n{content}"
if word_limit:
prompt += f"\n\nOutput MUST be {word_limit} words or less while maintaining complete sentences and natural flow."
prompt += "\nPlease provide the improved version of the text only. Do not include any explanations or metadata."
headers = {
"api-key": api_key,
"Content-Type": "application/json"
}
payload = create_payload(system_prompt, prompt, model)
try:
response = requests.post(api_url, headers=headers, data=json.dumps(payload))
response.raise_for_status()
result = response.json()
processed_content = result['choices'][0]['message']['content'].strip()
# Get detailed token usage from response
usage = result.get('usage', {})
token_usage = {
'prompt_tokens': usage.get('prompt_tokens', 0),
'completion_tokens': usage.get('completion_tokens', 0),
'total_tokens': usage.get('total_tokens', 0)
}
# Check if we need to retry with a more aggressive prompt for word limit
if word_limit and len(processed_content.split()) > word_limit:
retry_prompt = f"Revise to {word_limit} words or less. Output ONLY the edited text:\n\n{processed_content}"
retry_payload = create_payload(system_prompt, retry_prompt, model)
retry_response = requests.post(api_url, headers=headers, data=json.dumps(retry_payload))
retry_response.raise_for_status()
retry_result = retry_response.json()
processed_content = retry_result['choices'][0]['message']['content'].strip()
# Add retry tokens to total
retry_usage = retry_result.get('usage', {})
token_usage['prompt_tokens'] += retry_usage.get('prompt_tokens', 0)
token_usage['completion_tokens'] += retry_usage.get('completion_tokens', 0)
token_usage['total_tokens'] += retry_usage.get('total_tokens', 0)
return processed_content, token_usage
except Exception as e:
print(f"Error calling Azure OpenAI API: {str(e)}")
return content, {'prompt_tokens': 0, 'completion_tokens': 0, 'total_tokens': 0} # Return original content and 0 tokens if processing fails