mirror of
https://github.com/coleam00/Archon.git
synced 2025-12-24 02:39:17 -05:00
Fix Issue #248: Replace hardcoded OpenAI usage with unified LLM provider service
- Convert generate_code_example_summary() to async and use LLM provider service - Convert extract_source_summary() and generate_source_title_and_metadata() to async - Replace direct OpenAI client instantiation with get_llm_client() context manager - Update all function calls to use await for async functions - This enables Ollama support for code extraction and source summary generation Fixes: #248 - Ollama model code extraction now works properly
This commit is contained in:
@@ -33,7 +33,7 @@ def _get_model_choice() -> str:
|
||||
return "gpt-4.1-nano"
|
||||
|
||||
|
||||
def extract_source_summary(
|
||||
async def extract_source_summary(
|
||||
source_id: str, content: str, max_length: int = 500, provider: str = None
|
||||
) -> str:
|
||||
"""
|
||||
@@ -72,39 +72,14 @@ The above content is from the documentation for '{source_id}'. Please provide a
|
||||
"""
|
||||
|
||||
try:
|
||||
try:
|
||||
import os
|
||||
# Use unified LLM provider service instead of hardcoded OpenAI
|
||||
from .llm_provider_service import get_llm_client
|
||||
|
||||
import openai
|
||||
|
||||
api_key = os.getenv("OPENAI_API_KEY")
|
||||
if not api_key:
|
||||
# Try to get from credential service with direct fallback
|
||||
from .credential_service import credential_service
|
||||
|
||||
if (
|
||||
credential_service._cache_initialized
|
||||
and "OPENAI_API_KEY" in credential_service._cache
|
||||
):
|
||||
cached_key = credential_service._cache["OPENAI_API_KEY"]
|
||||
if isinstance(cached_key, dict) and cached_key.get("is_encrypted"):
|
||||
api_key = credential_service._decrypt_value(cached_key["encrypted_value"])
|
||||
else:
|
||||
api_key = cached_key
|
||||
else:
|
||||
api_key = os.getenv("OPENAI_API_KEY", "")
|
||||
|
||||
if not api_key:
|
||||
raise ValueError("No OpenAI API key available")
|
||||
|
||||
client = openai.OpenAI(api_key=api_key)
|
||||
search_logger.info("Successfully created LLM client fallback for summary generation")
|
||||
except Exception as e:
|
||||
search_logger.error(f"Failed to create LLM client fallback: {e}")
|
||||
return default_summary
|
||||
|
||||
# Call the OpenAI API to generate the summary
|
||||
response = client.chat.completions.create(
|
||||
async with get_llm_client(provider=provider) as client:
|
||||
search_logger.info("Successfully created LLM client for summary generation")
|
||||
|
||||
# Call the LLM API to generate the summary
|
||||
response = await client.chat.completions.create(
|
||||
model=model_choice,
|
||||
messages=[
|
||||
{
|
||||
@@ -140,7 +115,7 @@ The above content is from the documentation for '{source_id}'. Please provide a
|
||||
return default_summary
|
||||
|
||||
|
||||
def generate_source_title_and_metadata(
|
||||
async def generate_source_title_and_metadata(
|
||||
source_id: str,
|
||||
content: str,
|
||||
knowledge_type: str = "technical",
|
||||
@@ -166,40 +141,8 @@ def generate_source_title_and_metadata(
|
||||
# Try to generate a better title from content
|
||||
if content and len(content.strip()) > 100:
|
||||
try:
|
||||
try:
|
||||
import os
|
||||
|
||||
import openai
|
||||
|
||||
api_key = os.getenv("OPENAI_API_KEY")
|
||||
if not api_key:
|
||||
# Try to get from credential service with direct fallback
|
||||
from .credential_service import credential_service
|
||||
|
||||
if (
|
||||
credential_service._cache_initialized
|
||||
and "OPENAI_API_KEY" in credential_service._cache
|
||||
):
|
||||
cached_key = credential_service._cache["OPENAI_API_KEY"]
|
||||
if isinstance(cached_key, dict) and cached_key.get("is_encrypted"):
|
||||
api_key = credential_service._decrypt_value(
|
||||
cached_key["encrypted_value"]
|
||||
)
|
||||
else:
|
||||
api_key = cached_key
|
||||
else:
|
||||
api_key = os.getenv("OPENAI_API_KEY", "")
|
||||
|
||||
if not api_key:
|
||||
raise ValueError("No OpenAI API key available")
|
||||
|
||||
client = openai.OpenAI(api_key=api_key)
|
||||
except Exception as e:
|
||||
search_logger.error(
|
||||
f"Failed to create LLM client fallback for title generation: {e}"
|
||||
)
|
||||
# Don't proceed if client creation fails
|
||||
raise
|
||||
# Use unified LLM provider service instead of hardcoded OpenAI
|
||||
from .llm_provider_service import get_llm_client
|
||||
|
||||
model_choice = _get_model_choice()
|
||||
|
||||
@@ -215,7 +158,8 @@ def generate_source_title_and_metadata(
|
||||
|
||||
Provide only the title, nothing else."""
|
||||
|
||||
response = client.chat.completions.create(
|
||||
async with get_llm_client(provider=provider) as client:
|
||||
response = await client.chat.completions.create(
|
||||
model=model_choice,
|
||||
messages=[
|
||||
{
|
||||
@@ -247,7 +191,7 @@ Provide only the title, nothing else."""
|
||||
return title, metadata
|
||||
|
||||
|
||||
def update_source_info(
|
||||
async def update_source_info(
|
||||
client: Client,
|
||||
source_id: str,
|
||||
summary: str,
|
||||
@@ -351,7 +295,7 @@ def update_source_info(
|
||||
}
|
||||
else:
|
||||
# Fallback to AI generation only if no display name
|
||||
title, metadata = generate_source_title_and_metadata(
|
||||
title, metadata = await generate_source_title_and_metadata(
|
||||
source_id, content, knowledge_type, tags, None, source_display_name
|
||||
)
|
||||
|
||||
@@ -568,7 +512,7 @@ class SourceManagementService:
|
||||
logger.error(f"Error updating source metadata: {e}")
|
||||
return False, {"error": f"Error updating source metadata: {str(e)}"}
|
||||
|
||||
def create_source_info(
|
||||
async def create_source_info(
|
||||
self,
|
||||
source_id: str,
|
||||
content_sample: str,
|
||||
@@ -596,10 +540,10 @@ class SourceManagementService:
|
||||
tags = []
|
||||
|
||||
# Generate source summary using the utility function
|
||||
source_summary = extract_source_summary(source_id, content_sample)
|
||||
source_summary = await extract_source_summary(source_id, content_sample)
|
||||
|
||||
# Create the source info using the utility function
|
||||
update_source_info(
|
||||
await update_source_info(
|
||||
self.supabase_client,
|
||||
source_id,
|
||||
source_summary,
|
||||
|
||||
@@ -489,7 +489,7 @@ def extract_code_blocks(markdown_content: str, min_length: int = None) -> list[d
|
||||
return grouped_blocks
|
||||
|
||||
|
||||
def generate_code_example_summary(
|
||||
async def generate_code_example_summary(
|
||||
code: str, context_before: str, context_after: str, language: str = "", provider: str = None
|
||||
) -> dict[str, str]:
|
||||
"""
|
||||
@@ -535,82 +535,50 @@ Format your response as JSON:
|
||||
"""
|
||||
|
||||
try:
|
||||
# Get LLM client using fallback
|
||||
try:
|
||||
import os
|
||||
# Use unified LLM provider service instead of hardcoded OpenAI
|
||||
from ..llm_provider_service import get_llm_client
|
||||
|
||||
import openai
|
||||
|
||||
api_key = os.getenv("OPENAI_API_KEY")
|
||||
if not api_key:
|
||||
# Try to get from credential service with direct fallback
|
||||
from ..credential_service import credential_service
|
||||
|
||||
if (
|
||||
credential_service._cache_initialized
|
||||
and "OPENAI_API_KEY" in credential_service._cache
|
||||
):
|
||||
cached_key = credential_service._cache["OPENAI_API_KEY"]
|
||||
if isinstance(cached_key, dict) and cached_key.get("is_encrypted"):
|
||||
api_key = credential_service._decrypt_value(cached_key["encrypted_value"])
|
||||
else:
|
||||
api_key = cached_key
|
||||
else:
|
||||
api_key = os.getenv("OPENAI_API_KEY", "")
|
||||
|
||||
if not api_key:
|
||||
raise ValueError("No OpenAI API key available")
|
||||
|
||||
client = openai.OpenAI(api_key=api_key)
|
||||
except Exception as e:
|
||||
search_logger.error(
|
||||
f"Failed to create LLM client fallback: {e} - returning default values"
|
||||
async with get_llm_client(provider=provider) as client:
|
||||
search_logger.debug(
|
||||
f"Calling LLM API with model: {model_choice}, language: {language}, code length: {len(code)}"
|
||||
)
|
||||
return {
|
||||
"example_name": f"Code Example{f' ({language})' if language else ''}",
|
||||
"summary": "Code example for demonstration purposes.",
|
||||
|
||||
response = await client.chat.completions.create(
|
||||
model=model_choice,
|
||||
messages=[
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a helpful assistant that analyzes code examples and provides JSON responses with example names and summaries.",
|
||||
},
|
||||
{"role": "user", "content": prompt},
|
||||
],
|
||||
response_format={"type": "json_object"},
|
||||
)
|
||||
|
||||
response_content = response.choices[0].message.content.strip()
|
||||
search_logger.debug(f"LLM API response: {repr(response_content[:200])}...")
|
||||
|
||||
result = json.loads(response_content)
|
||||
|
||||
# Validate the response has the required fields
|
||||
if not result.get("example_name") or not result.get("summary"):
|
||||
search_logger.warning(f"Incomplete response from LLM: {result}")
|
||||
|
||||
final_result = {
|
||||
"example_name": result.get(
|
||||
"example_name", f"Code Example{f' ({language})' if language else ''}"
|
||||
),
|
||||
"summary": result.get("summary", "Code example for demonstration purposes."),
|
||||
}
|
||||
|
||||
search_logger.debug(
|
||||
f"Calling OpenAI API with model: {model_choice}, language: {language}, code length: {len(code)}"
|
||||
)
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model=model_choice,
|
||||
messages=[
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a helpful assistant that analyzes code examples and provides JSON responses with example names and summaries.",
|
||||
},
|
||||
{"role": "user", "content": prompt},
|
||||
],
|
||||
response_format={"type": "json_object"},
|
||||
)
|
||||
|
||||
response_content = response.choices[0].message.content.strip()
|
||||
search_logger.debug(f"OpenAI API response: {repr(response_content[:200])}...")
|
||||
|
||||
result = json.loads(response_content)
|
||||
|
||||
# Validate the response has the required fields
|
||||
if not result.get("example_name") or not result.get("summary"):
|
||||
search_logger.warning(f"Incomplete response from OpenAI: {result}")
|
||||
|
||||
final_result = {
|
||||
"example_name": result.get(
|
||||
"example_name", f"Code Example{f' ({language})' if language else ''}"
|
||||
),
|
||||
"summary": result.get("summary", "Code example for demonstration purposes."),
|
||||
}
|
||||
|
||||
search_logger.info(
|
||||
f"Generated code example summary - Name: '{final_result['example_name']}', Summary length: {len(final_result['summary'])}"
|
||||
)
|
||||
return final_result
|
||||
search_logger.info(
|
||||
f"Generated code example summary - Name: '{final_result['example_name']}', Summary length: {len(final_result['summary'])}"
|
||||
)
|
||||
return final_result
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
search_logger.error(
|
||||
f"Failed to parse JSON response from OpenAI: {e}, Response: {repr(response_content) if 'response_content' in locals() else 'No response'}"
|
||||
f"Failed to parse JSON response from LLM: {e}, Response: {repr(response_content) if 'response_content' in locals() else 'No response'}"
|
||||
)
|
||||
return {
|
||||
"example_name": f"Code Example{f' ({language})' if language else ''}",
|
||||
@@ -671,11 +639,8 @@ async def generate_code_summaries_batch(
|
||||
# Add delay between requests to avoid rate limiting
|
||||
await asyncio.sleep(0.5) # 500ms delay between requests
|
||||
|
||||
# Run the synchronous function in a thread
|
||||
loop = asyncio.get_event_loop()
|
||||
result = await loop.run_in_executor(
|
||||
None,
|
||||
generate_code_example_summary,
|
||||
# Call the async function directly
|
||||
result = await generate_code_example_summary(
|
||||
block["code"],
|
||||
block["context_before"],
|
||||
block["context_after"],
|
||||
|
||||
Reference in New Issue
Block a user