kjj
This commit is contained in:
parent
ff4b399794
commit
2ffafb4508
224
gurt/api.py
224
gurt/api.py
@ -1143,106 +1143,150 @@ async def get_ai_response(cog: 'GurtCog', message: discord.Message, model_name:
|
||||
elif tool_calls_made >= max_tool_calls:
|
||||
error_message = f"Reached maximum tool call limit ({max_tool_calls}). Attempting to generate final response based on gathered context."
|
||||
print(error_message)
|
||||
# Proceed to the final JSON generation step outside the loop
|
||||
# Proceed to the final generation step outside the loop
|
||||
pass # No action needed here, just let the loop exit
|
||||
|
||||
# --- Final JSON Generation (outside the loop) ---
|
||||
# --- Final Parallel Generation (outside the loop) ---
|
||||
if not error_message:
|
||||
# If the loop finished because no more tools were called, the last_response_obj
|
||||
# should contain the final textual response (potentially JSON).
|
||||
if last_response_obj:
|
||||
print("Attempting to parse final JSON from the last response object...")
|
||||
last_response_text = _get_response_text(last_response_obj)
|
||||
print("Entering final parallel generation step...")
|
||||
# Prepare two generation configs: one for schema, one for plain text
|
||||
|
||||
# --- Log Raw Unparsed JSON (from loop exit) ---
|
||||
print(f"--- RAW UNPARSED JSON (from loop exit) ---")
|
||||
print(last_response_text)
|
||||
print(f"--- END RAW UNPARSED JSON ---")
|
||||
# --- End Log ---
|
||||
# Config for Schema Generation
|
||||
processed_response_schema = _preprocess_schema_for_vertex(RESPONSE_SCHEMA['schema'])
|
||||
config_schema_dict = base_generation_config_dict.copy()
|
||||
config_schema_dict.update({
|
||||
"response_mime_type": "application/json",
|
||||
"response_schema": processed_response_schema,
|
||||
"tools": None,
|
||||
"tool_config": None
|
||||
})
|
||||
# Remove system_instruction if None/empty (should be present from base)
|
||||
if not config_schema_dict.get("system_instruction"):
|
||||
config_schema_dict.pop("system_instruction", None)
|
||||
generation_config_schema = types.GenerateContentConfig(**config_schema_dict)
|
||||
|
||||
if last_response_text:
|
||||
# Try parsing directly first
|
||||
final_parsed_data = parse_and_validate_json_response(
|
||||
last_response_text, RESPONSE_SCHEMA['schema'], "final response (from last loop object)"
|
||||
)
|
||||
# Config for Plain Text Generation
|
||||
config_plain_text_dict = base_generation_config_dict.copy()
|
||||
config_plain_text_dict.update({
|
||||
# Omit schema-related fields for plain text
|
||||
"response_mime_type": None, # Or ensure it's not set / set to "text/plain" if needed
|
||||
"response_schema": None, # Or ensure it's not set
|
||||
"tools": None,
|
||||
"tool_config": None
|
||||
})
|
||||
# Remove system_instruction if None/empty
|
||||
if not config_plain_text_dict.get("system_instruction"):
|
||||
config_plain_text_dict.pop("system_instruction", None)
|
||||
generation_config_plain_text = types.GenerateContentConfig(**config_plain_text_dict)
|
||||
|
||||
# If direct parsing failed OR if we hit the tool limit, make a dedicated call for JSON.
|
||||
if final_parsed_data is None:
|
||||
log_reason = "last response parsing failed" if last_response_text else "last response had no text"
|
||||
if tool_calls_made >= max_tool_calls:
|
||||
log_reason = "hit tool limit"
|
||||
print(f"Making dedicated final API call for JSON ({log_reason})...")
|
||||
# --- Create and Run Parallel Tasks ---
|
||||
print("Launching parallel API calls for schema and plain text...")
|
||||
schema_call_task = call_google_genai_api_with_retry(
|
||||
cog=cog,
|
||||
model_name=final_response_model,
|
||||
contents=contents,
|
||||
generation_config=generation_config_schema,
|
||||
request_desc=f"Final JSON Schema Generation for message {message.id}"
|
||||
)
|
||||
plain_text_call_task = call_google_genai_api_with_retry(
|
||||
cog=cog,
|
||||
model_name=final_response_model, # Use the same model for consistency? Or a cheaper one?
|
||||
contents=contents,
|
||||
generation_config=generation_config_plain_text,
|
||||
request_desc=f"Final Plain Text Generation for message {message.id}"
|
||||
)
|
||||
|
||||
# Prepare the final generation config with JSON enforcement
|
||||
processed_response_schema = _preprocess_schema_for_vertex(RESPONSE_SCHEMA['schema'])
|
||||
# Start with base config (which now includes system_instruction)
|
||||
final_gen_config_dict = base_generation_config_dict.copy()
|
||||
final_gen_config_dict.update({
|
||||
# "response_mime_type": "application/json",
|
||||
# "response_schema": processed_response_schema,
|
||||
# Explicitly exclude tools/tool_config for final JSON generation
|
||||
"tools": None,
|
||||
"tool_config": None,
|
||||
# Ensure system_instruction is still present from base_generation_config_dict
|
||||
})
|
||||
# Remove system_instruction if it's None or empty, although base should have it
|
||||
if not final_gen_config_dict.get("system_instruction"):
|
||||
final_gen_config_dict.pop("system_instruction", None)
|
||||
results = await asyncio.gather(schema_call_task, plain_text_call_task, return_exceptions=True)
|
||||
schema_result, plain_text_result = results
|
||||
print("Parallel API calls completed.")
|
||||
|
||||
generation_config_final_json = types.GenerateContentConfig(**final_gen_config_dict)
|
||||
# --- Process Results ---
|
||||
parsed_schema_data = None
|
||||
extracted_plain_text = None
|
||||
schema_error = None
|
||||
plain_text_error = None
|
||||
|
||||
|
||||
# Make the final call *without* tools enabled (handled by config)
|
||||
final_json_response_obj = await call_google_genai_api_with_retry(
|
||||
cog=cog,
|
||||
model_name=final_response_model, # Use the CUSTOM_TUNED_MODEL for final response
|
||||
contents=contents, # Pass the accumulated history
|
||||
generation_config=generation_config_final_json, # Use combined JSON config
|
||||
request_desc=f"Final JSON Generation (dedicated call) for message {message.id}",
|
||||
# No separate safety, tools, tool_config args needed
|
||||
)
|
||||
|
||||
if not final_json_response_obj:
|
||||
error_msg_suffix = "Final dedicated API call returned no response object."
|
||||
print(error_msg_suffix)
|
||||
if error_message: error_message += f" | {error_msg_suffix}"
|
||||
else: error_message = error_msg_suffix
|
||||
elif not final_json_response_obj.candidates:
|
||||
error_msg_suffix = "Final dedicated API call returned no candidates."
|
||||
print(error_msg_suffix)
|
||||
if error_message: error_message += f" | {error_msg_suffix}"
|
||||
else: error_message = error_msg_suffix
|
||||
else:
|
||||
final_response_text = _get_response_text(final_json_response_obj)
|
||||
|
||||
# --- Log Raw Unparsed JSON (from dedicated call) ---
|
||||
print(f"--- RAW UNPARSED JSON (dedicated call) ---")
|
||||
print(final_response_text)
|
||||
print(f"--- END RAW UNPARSED JSON ---")
|
||||
# --- End Log ---
|
||||
|
||||
final_parsed_data = parse_and_validate_json_response(
|
||||
final_response_text, RESPONSE_SCHEMA['schema'], "final response (dedicated call)"
|
||||
)
|
||||
if final_parsed_data is None:
|
||||
error_msg_suffix = f"Failed to parse/validate final dedicated JSON response. Raw text: {final_response_text[:500]}"
|
||||
print(f"Critical Error: {error_msg_suffix}")
|
||||
if error_message: error_message += f" | {error_msg_suffix}"
|
||||
else: error_message = error_msg_suffix
|
||||
# Set fallback only if mentioned or replied to
|
||||
if cog.bot.user.mentioned_in(message) or (message.reference and message.reference.resolved and message.reference.resolved.author == cog.bot.user):
|
||||
fallback_response = {"should_respond": True, "content": "...", "react_with_emoji": "❓"}
|
||||
else:
|
||||
print("Successfully parsed final JSON response from dedicated call.")
|
||||
elif final_parsed_data:
|
||||
print("Successfully parsed final JSON response from last loop object.")
|
||||
# Process Schema Result
|
||||
if isinstance(schema_result, Exception):
|
||||
schema_error = f"Schema generation API call failed: {type(schema_result).__name__}: {schema_result}"
|
||||
print(schema_error)
|
||||
error_message = (error_message + f" | {schema_error}") if error_message else schema_error
|
||||
elif not schema_result or not schema_result.candidates:
|
||||
schema_error = "Schema generation API call returned no candidates."
|
||||
print(schema_error)
|
||||
error_message = (error_message + f" | {schema_error}") if error_message else schema_error
|
||||
else:
|
||||
# This case handles if the loop exited without error but also without a last_response_obj
|
||||
# (e.g., initial API call failed before loop even started, but wasn't caught as error).
|
||||
error_message = "Tool processing completed without a final response object."
|
||||
print(error_message)
|
||||
if cog.bot.user.mentioned_in(message) or (message.reference and message.reference.resolved and message.reference.resolved.author == cog.bot.user):
|
||||
fallback_response = {"should_respond": True, "content": "...", "react_with_emoji": "❓"}
|
||||
schema_response_text = _get_response_text(schema_result)
|
||||
print(f"--- RAW UNPARSED JSON (parallel call) ---")
|
||||
print(schema_response_text)
|
||||
print(f"--- END RAW UNPARSED JSON ---")
|
||||
parsed_schema_data = parse_and_validate_json_response(
|
||||
schema_response_text, RESPONSE_SCHEMA['schema'], "final response (parallel schema call)"
|
||||
)
|
||||
if parsed_schema_data is None:
|
||||
schema_error = f"Failed to parse/validate final parallel JSON response. Raw text: {schema_response_text[:500]}"
|
||||
print(f"Critical Error: {schema_error}")
|
||||
error_message = (error_message + f" | {schema_error}") if error_message else schema_error
|
||||
|
||||
# Process Plain Text Result
|
||||
if isinstance(plain_text_result, Exception):
|
||||
plain_text_error = f"Plain text generation API call failed: {type(plain_text_result).__name__}: {plain_text_result}"
|
||||
print(plain_text_error)
|
||||
error_message = (error_message + f" | {plain_text_error}") if error_message else plain_text_error
|
||||
elif not plain_text_result or not plain_text_result.candidates:
|
||||
plain_text_error = "Plain text generation API call returned no candidates."
|
||||
print(plain_text_error)
|
||||
error_message = (error_message + f" | {plain_text_error}") if error_message else plain_text_error
|
||||
else:
|
||||
extracted_plain_text = _get_response_text(plain_text_result)
|
||||
print(f"--- Extracted Plain Text (parallel call) ---")
|
||||
print(extracted_plain_text)
|
||||
print(f"--- END Extracted Plain Text ---")
|
||||
if not extracted_plain_text or not extracted_plain_text.strip():
|
||||
print("Warning: Plain text generation resulted in empty content.")
|
||||
# Keep extracted_plain_text as None or empty string
|
||||
|
||||
# --- Combine Results ---
|
||||
if parsed_schema_data:
|
||||
print("Successfully parsed schema data.")
|
||||
if extracted_plain_text and extracted_plain_text.strip():
|
||||
print("Overwriting 'content' in parsed schema with extracted plain text.")
|
||||
parsed_schema_data['content'] = extracted_plain_text.strip() # Use stripped plain text
|
||||
else:
|
||||
print("No valid plain text extracted, keeping original 'content' from schema (if any).")
|
||||
# Ensure content key exists even if plain text was empty
|
||||
parsed_schema_data.setdefault('content', None)
|
||||
final_parsed_data = parsed_schema_data # Use the (potentially modified) schema data
|
||||
elif extracted_plain_text and extracted_plain_text.strip():
|
||||
print("Schema parsing failed, but plain text is available. Creating fallback response.")
|
||||
# Create a default structure using the plain text
|
||||
final_parsed_data = {
|
||||
"should_respond": True,
|
||||
"content": extracted_plain_text.strip(),
|
||||
"react_with_emoji": None,
|
||||
# Add other default fields from RESPONSE_SCHEMA if needed
|
||||
"thinking_process": "Fallback due to schema failure.",
|
||||
"confidence_score": 0.5,
|
||||
"suggested_follow_up": None,
|
||||
"internal_state_update": None,
|
||||
"metadata": {"schema_error": schema_error, "plain_text_error": plain_text_error}
|
||||
}
|
||||
else:
|
||||
# Both failed or produced no usable output
|
||||
print("Both schema and plain text generation failed or yielded no content.")
|
||||
if not error_message: # If no specific API error was logged, add a generic one
|
||||
error_message = "Failed to generate a valid response from either schema or plain text calls."
|
||||
# Set fallback only if mentioned or replied to, based on overall error state
|
||||
if cog.bot.user.mentioned_in(message) or (message.reference and message.reference.resolved and message.reference.resolved.author == cog.bot.user):
|
||||
fallback_response = {"should_respond": True, "content": "...", "react_with_emoji": "❓"}
|
||||
|
||||
# --- Error Handling Catch-All ---
|
||||
else: # This 'else' corresponds to 'if not error_message:' before parallel generation
|
||||
# This means an error occurred *before* the parallel generation step (e.g., loop error)
|
||||
print(f"Skipping final parallel generation due to pre-existing error: {error_message}")
|
||||
# Set fallback based on the pre-existing error
|
||||
if cog.bot.user.mentioned_in(message) or (message.reference and message.reference.resolved and message.reference.resolved.author == cog.bot.user):
|
||||
fallback_response = {"should_respond": True, "content": "...", "react_with_emoji": "❓"}
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
Loading…
x
Reference in New Issue
Block a user