modificari mari

This commit is contained in:
Daniel Bedeleanu
2026-04-15 15:20:45 +03:00
parent 6102ff39aa
commit 1893c4f38b
15 changed files with 1154 additions and 898 deletions

View File

@@ -73,8 +73,18 @@ def extract_label_info(image_bytes: bytes, mode: str = "item"):
# 3. Final fallback to hardcoded default
prompt = "Extract technical specs. Return JSON with name, category, description, connector, size, color, part_number, ocr_text, quantity."
# 1. Try Gemini
result = gemini.extract(image_bytes, prompt)
# 0. Get Active Provider from DB
provider_setting = db.query(models.SystemSetting).filter(models.SystemSetting.key == "ai_provider").first()
active_provider = provider_setting.value if provider_setting else "gemini"
# 1. Execute extraction based on selection
result = None
if active_provider == "claude":
print(f"📡 Using Anthropic Claude for extraction...")
result = claude.extract(image_bytes, prompt)
else:
print(f"📡 Using Google Gemini for extraction...")
result = gemini.extract(image_bytes, prompt)
if result:
# Check if AI returned a list of items or a singular object

90
backend/config_manager.py Normal file
View File

@@ -0,0 +1,90 @@
import os
from dotenv import load_dotenv
class ConfigManager:
"""Safely manages multi-line .env files without corrupting other content."""
@staticmethod
def get_root_env_path():
# backend/config_manager.py -> backend/ -> /
base_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(base_dir)
return os.path.join(project_root, "inventory.env")
@staticmethod
def update_keys(updates: dict):
"""
Updates specific keys in inventory.env.
Preserves comments and order where possible.
Appends new keys at the end if not found.
"""
env_path = ConfigManager.get_root_env_path()
if not os.path.exists(env_path):
# Create a basic file if it doesn't exist (unlikely in this project)
with open(env_path, 'w', encoding='utf-8') as f:
f.write("# TFM aInventory — Generated Configuration\n")
with open(env_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
new_lines = []
keys_to_process = set(updates.keys())
processed_keys = set()
for line in lines:
trimmed = line.strip()
# Skip empty lines or comments when matching
if not trimmed or trimmed.startswith('#'):
new_lines.append(line)
continue
# Check if this line is a key assignment we want to update
found_match = False
for key in keys_to_process:
if trimmed.startswith(f"{key}="):
new_lines.append(f"{key}={updates[key]}\n")
processed_keys.add(key)
found_match = True
break
if not found_match:
new_lines.append(line)
# Append keys that weren't found in the file
missing_keys = keys_to_process - processed_keys
if missing_keys:
if new_lines and not new_lines[-1].endswith('\n'):
new_lines.append('\n')
if new_lines and not new_lines[-1].strip() == '':
new_lines.append('\n')
new_lines.append("# --- Automatically Added Keys ---\n")
for key in missing_keys:
new_lines.append(f"{key}={updates[key]}\n")
with open(env_path, 'w', encoding='utf-8') as f:
f.writelines(new_lines)
# Force reload environment variables for the current process
load_dotenv(env_path, override=True)
return True
@staticmethod
def get_masked_key(key_name: str):
"""Returns a masked version of the environment variable."""
val = os.environ.get(key_name)
if not val:
return None
# Determine prefix based on key type
prefix = ""
if key_name == "GEMINI_API_KEY":
prefix = "G-"
elif key_name == "CLAUDE_API_KEY":
prefix = "sk-"
if len(val) <= 8:
return f"{prefix}****"
return f"{prefix}****{val[-4:]}"

View File

@@ -145,7 +145,8 @@ def startup_event():
defaults = {
"backup_retention_count": "10",
"backup_schedule_hour": "3",
"backup_schedule_freq_days": "1"
"backup_schedule_freq_days": "1",
"ai_provider": "gemini"
}
for key, val in defaults.items():
if not db.query(models.SystemSetting).filter(models.SystemSetting.key == key).first():

View File

@@ -189,3 +189,126 @@ def update_ai_prompt(
db.commit()
return {"status": "success", "file_updated": os.path.exists(PROMPT_FILE_PATH)}
from ..config_manager import ConfigManager
@router.get("/settings/ai")
def get_ai_config(
db: Session = Depends(get_db),
current_admin: auth.TokenData = Depends(auth.get_current_admin)
):
"""Check AI provider status and active provider."""
gemini_key = os.environ.get("GEMINI_API_KEY")
claude_key = os.environ.get("CLAUDE_API_KEY")
# Get active provider from DB
provider_setting = db.query(models.SystemSetting).filter(models.SystemSetting.key == "ai_provider").first()
active_provider = provider_setting.value if provider_setting else "gemini"
return {
"active_provider": active_provider,
"providers": [
{
"id": "gemini",
"name": "Google Gemini 2.0",
"configured": bool(gemini_key),
"active": active_provider == "gemini",
"masked_key": ConfigManager.get_masked_key("GEMINI_API_KEY")
},
{
"id": "claude",
"name": "Anthropic Claude 3.5",
"configured": bool(claude_key),
"active": active_provider == "claude",
"masked_key": ConfigManager.get_masked_key("CLAUDE_API_KEY")
}
]
}
@router.post("/settings/ai-keys")
def update_ai_keys(
payload: dict,
current_admin: auth.TokenData = Depends(auth.get_current_admin)
):
"""Update AI API keys in inventory.env."""
gemini_key = payload.get("gemini_api_key")
claude_key = payload.get("claude_api_key")
updates = {}
if gemini_key:
updates["GEMINI_API_KEY"] = gemini_key
if claude_key:
updates["CLAUDE_API_KEY"] = claude_key
if not updates:
return {"status": "no_change"}
ConfigManager.update_keys(updates)
return {
"status": "success",
"gemini_configured": bool(os.environ.get("GEMINI_API_KEY")),
"claude_configured": bool(os.environ.get("CLAUDE_API_KEY"))
}
@router.post("/settings/test-ai-key")
def test_ai_key(
payload: dict,
current_admin: auth.TokenData = Depends(auth.get_current_admin)
):
"""Test AI API key connectivity."""
provider = payload.get("provider")
key = payload.get("key")
if not provider or provider not in ["gemini", "claude"]:
raise HTTPException(status_code=400, detail="Invalid provider")
# If key is masked or empty, use the one from environment
if not key or "****" in key:
key = os.environ.get("GEMINI_API_KEY" if provider == "gemini" else "CLAUDE_API_KEY")
if not key:
raise HTTPException(status_code=400, detail="No API key provided or configured")
try:
if provider == "gemini":
from google import genai
client = genai.Client(api_key=key, http_options={'api_version': 'v1beta'})
# Listing models is the standard way to check key validity
client.models.list()
return {"status": "success", "message": "Google Gemini API connection verified!"}
elif provider == "claude":
import anthropic
client = anthropic.Anthropic(api_key=key)
# Claude also supports model listing for key verification
client.models.list(limit=1)
return {"status": "success", "message": "Anthropic Claude API connection verified!"}
except Exception as e:
error_msg = str(e)
if "API_KEY_INVALID" in error_msg or "invalid_api_key" in error_msg:
error_msg = "The API key provided is invalid."
elif "PERMISSION_DENIED" in error_msg:
error_msg = "Permission denied. Check if the key has correct permissions."
raise HTTPException(status_code=400, detail=f"{provider.capitalize()} Test Failed: {error_msg}")
@router.post("/settings/ai")
def update_ai_provider(
payload: dict,
db: Session = Depends(get_db),
current_admin: auth.TokenData = Depends(auth.get_current_admin)
):
"""Update the active AI provider."""
provider = payload.get("provider")
if provider not in ["gemini", "claude"]:
raise HTTPException(status_code=400, detail="Invalid provider. Must be 'gemini' or 'claude'.")
existing = db.query(models.SystemSetting).filter(models.SystemSetting.key == "ai_provider").first()
if existing:
existing.value = provider
else:
db.add(models.SystemSetting(key="ai_provider", value=provider))
db.commit()
return {"status": "success", "active_provider": provider}