SUBHRAJIT MOHANTY commited on
Commit
c3ff739
·
1 Parent(s): 80a4ae3

Updated app.py with dynamic API call

Browse files
Files changed (1) hide show
  1. app.py +256 -51
app.py CHANGED
@@ -10,9 +10,10 @@ import os
10
  from contextlib import asynccontextmanager
11
  import tempfile
12
  import shutil
 
13
 
14
  # Third-party imports
15
- from openai import AsyncOpenAI
16
  from qdrant_client import AsyncQdrantClient
17
  from qdrant_client.models import Distance, VectorParams, PointStruct, Filter, FieldCondition, MatchValue
18
  from sentence_transformers import SentenceTransformer
@@ -27,12 +28,13 @@ class Message(BaseModel):
27
  content: str = Field(..., description="The content of the message")
28
 
29
  class ChatCompletionRequest(BaseModel):
30
- model: str = Field(default="mixtral-8x7b-32768", description="Model to use")
31
  messages: List[Message] = Field(..., description="List of messages")
32
  max_tokens: Optional[int] = Field(default=1024, description="Maximum tokens to generate")
33
  temperature: Optional[float] = Field(default=0.7, description="Temperature for sampling")
34
  stream: Optional[bool] = Field(default=False, description="Whether to stream responses")
35
  top_p: Optional[float] = Field(default=1.0, description="Top-p sampling parameter")
 
36
 
37
  class ChatCompletionResponse(BaseModel):
38
  id: str
@@ -59,20 +61,152 @@ class DocumentSearchRequest(BaseModel):
59
 
60
  # Configuration
61
  class Config:
 
 
62
  GROQ_API_KEY = os.getenv("GROQ_API_KEY")
63
- GROQ_BASE_URL = os.getenv("GROQ_BASE_URL", "https://api.groq.com/openai/v1")
 
64
  QDRANT_URL = os.getenv("QDRANT_URL", "http://localhost:6333")
65
  QDRANT_API_KEY = os.getenv("QDRANT_API_KEY")
66
  COLLECTION_NAME = os.getenv("COLLECTION_NAME", "documents")
 
 
67
  EMBEDDING_MODEL = os.getenv("EMBEDDING_MODEL", "sentence-transformers/all-MiniLM-L6-v2")
68
- TOP_K = int(os.getenv("TOP_K", "10")) # Increased from 5
69
- SIMILARITY_THRESHOLD = float(os.getenv("SIMILARITY_THRESHOLD", "0.1")) # Lowered from 0.7
70
  DEVICE = os.getenv("DEVICE", "cuda" if torch.cuda.is_available() else "cpu")
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  class ApplicationState:
73
  """Application state container"""
74
  def __init__(self):
75
- self.openai_client = None
76
  self.qdrant_client = None
77
  self.embedding_service = None
78
  self.document_manager = None
@@ -421,7 +555,6 @@ class RAGService:
421
  return []
422
 
423
  # Use a lower similarity threshold for RAG to get more results
424
- # Try multiple thresholds if needed
425
  min_score = 0.1 # Lower threshold for RAG
426
 
427
  print(f"RAG Search - Query: '{query}', Limit: {top_k}, Min Score: {min_score}")
@@ -457,7 +590,7 @@ class RAGService:
457
  if not results:
458
  return query
459
 
460
- # Build context parts like in your example
461
  context_parts = []
462
  for result in results:
463
  context_parts.append(f"Source: {result['file_path']}\n{result['text']}")
@@ -478,25 +611,14 @@ Answer the question directly and naturally. Also Respond it in Markdown Format""
478
  @asynccontextmanager
479
  async def lifespan(app: FastAPI):
480
  # Startup
481
- if not Config.GROQ_API_KEY:
482
- raise ValueError("GROQ_API_KEY environment variable is required")
483
-
484
  print("Initializing services...")
485
 
486
- # Initialize OpenAI client with Groq endpoint
487
  try:
488
- print(f"Configuring OpenAI client with:")
489
- print(f" Base URL: {Config.GROQ_BASE_URL}")
490
- print(f" API Key: {'*' * 10}...{Config.GROQ_API_KEY[-4:] if Config.GROQ_API_KEY else 'None'}")
491
-
492
- app_state.openai_client = AsyncOpenAI(
493
- api_key=Config.GROQ_API_KEY,
494
- base_url=Config.GROQ_BASE_URL,
495
- timeout=60.0
496
- )
497
- print("✓ OpenAI client initialized with Groq endpoint")
498
  except Exception as e:
499
- print(f"✗ Error initializing OpenAI client: {e}")
500
  raise e
501
 
502
  # Initialize Qdrant client
@@ -541,9 +663,6 @@ async def lifespan(app: FastAPI):
541
  if app_state.qdrant_client:
542
  await app_state.qdrant_client.close()
543
  print("✓ Qdrant client closed")
544
- if app_state.openai_client:
545
- await app_state.openai_client.close()
546
- print("✓ OpenAI client closed")
547
  if app_state.embedding_service and hasattr(app_state.embedding_service, 'executor'):
548
  app_state.embedding_service.executor.shutdown(wait=True)
549
  print("✓ Embedding service executor shutdown")
@@ -554,15 +673,15 @@ async def lifespan(app: FastAPI):
554
 
555
  # Initialize FastAPI app
556
  app = FastAPI(
557
- title="Enhanced RAG API with Document Management",
558
- description="OpenAI-compatible API for RAG with document management using Groq and Qdrant",
559
  version="1.0.0",
560
  lifespan=lifespan
561
  )
562
 
563
  @app.get("/")
564
  async def root():
565
- return {"message": "Enhanced RAG API with Document Management", "status": "running"}
566
 
567
  @app.get("/health")
568
  async def health_check():
@@ -586,37 +705,61 @@ async def health_check():
586
  except Exception as e:
587
  embedding_health = {"status": "error", "error": str(e)}
588
 
589
- # Test OpenAI client
590
- if app_state.openai_client is None:
591
- openai_health = {"status": "not_initialized", "error": "OpenAI client is None"}
592
  else:
593
  try:
594
- test_response = await app_state.openai_client.chat.completions.create(
595
- model="mixtral-8x7b-32768",
596
- messages=[{"role": "user", "content": "test"}],
597
- max_tokens=1
598
- )
599
- openai_health = {"status": "healthy", "test_response_id": test_response.id}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
600
  except Exception as e:
601
  openai_health = {"status": "error", "error": str(e)}
602
 
603
  return {
604
  "status": "healthy" if app_state.embedding_service is not None else "unhealthy",
605
- "openai_client": openai_health,
606
  "qdrant": qdrant_status,
607
  "embedding_service": embedding_health,
608
  "document_manager": "initialized" if app_state.document_manager else "not_initialized",
609
  "collection": Config.COLLECTION_NAME,
610
  "embedding_model": Config.EMBEDDING_MODEL,
611
- "groq_endpoint": Config.GROQ_BASE_URL
 
 
 
612
  }
613
 
614
  @app.post("/v1/chat/completions")
615
  async def chat_completions(request: ChatCompletionRequest):
616
- """OpenAI-compatible chat completions endpoint with enhanced RAG"""
617
 
618
- if not app_state.openai_client:
619
- raise HTTPException(status_code=500, detail="OpenAI client not initialized")
620
 
621
  try:
622
  # Get the last user message for retrieval
@@ -665,10 +808,21 @@ async def chat_completions(request: ChatCompletionRequest):
665
  raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
666
 
667
  async def create_chat_completion(messages: List[Dict], request: ChatCompletionRequest) -> ChatCompletionResponse:
668
- """Create a non-streaming chat completion"""
669
  try:
670
- response = await app_state.openai_client.chat.completions.create(
671
- model=request.model,
 
 
 
 
 
 
 
 
 
 
 
672
  messages=messages,
673
  max_tokens=request.max_tokens,
674
  temperature=request.temperature,
@@ -679,7 +833,7 @@ async def create_chat_completion(messages: List[Dict], request: ChatCompletionRe
679
  result = ChatCompletionResponse(
680
  id=response.id,
681
  created=response.created,
682
- model=response.model,
683
  choices=[{
684
  "index": choice.index,
685
  "message": {
@@ -702,10 +856,21 @@ async def create_chat_completion(messages: List[Dict], request: ChatCompletionRe
702
  raise HTTPException(status_code=500, detail=f"Error calling OpenAI API: {str(e)}")
703
 
704
  async def stream_chat_completion(messages: List[Dict], request: ChatCompletionRequest) -> AsyncGenerator[str, None]:
705
- """Stream chat completion responses"""
706
  try:
707
- stream = await app_state.openai_client.chat.completions.create(
708
- model=request.model,
 
 
 
 
 
 
 
 
 
 
 
709
  messages=messages,
710
  max_tokens=request.max_tokens,
711
  temperature=request.temperature,
@@ -720,7 +885,7 @@ async def stream_chat_completion(messages: List[Dict], request: ChatCompletionRe
720
  chunk_response = ChatCompletionChunk(
721
  id=chunk.id,
722
  created=chunk.created,
723
- model=chunk.model,
724
  choices=[{
725
  "index": choice.index,
726
  "delta": {
@@ -912,6 +1077,46 @@ async def get_collection_info():
912
  except Exception as e:
913
  raise HTTPException(status_code=500, detail=f"Error getting collection info: {str(e)}")
914
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
915
  if __name__ == "__main__":
916
  import uvicorn
917
  uvicorn.run(app, host="0.0.0.0", port=8000)
 
10
  from contextlib import asynccontextmanager
11
  import tempfile
12
  import shutil
13
+ import random
14
 
15
  # Third-party imports
16
+ from openai import OpenAI, AsyncOpenAI
17
  from qdrant_client import AsyncQdrantClient
18
  from qdrant_client.models import Distance, VectorParams, PointStruct, Filter, FieldCondition, MatchValue
19
  from sentence_transformers import SentenceTransformer
 
28
  content: str = Field(..., description="The content of the message")
29
 
30
  class ChatCompletionRequest(BaseModel):
31
+ model: str = Field(default="auto", description="Model to use (auto for dynamic selection)")
32
  messages: List[Message] = Field(..., description="List of messages")
33
  max_tokens: Optional[int] = Field(default=1024, description="Maximum tokens to generate")
34
  temperature: Optional[float] = Field(default=0.7, description="Temperature for sampling")
35
  stream: Optional[bool] = Field(default=False, description="Whether to stream responses")
36
  top_p: Optional[float] = Field(default=1.0, description="Top-p sampling parameter")
37
+ provider: Optional[str] = Field(default="random", description="Provider to use (random, openrouter, groq)")
38
 
39
  class ChatCompletionResponse(BaseModel):
40
  id: str
 
61
 
62
  # Configuration
63
  class Config:
64
+ # Provider API Keys
65
+ OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")
66
  GROQ_API_KEY = os.getenv("GROQ_API_KEY")
67
+
68
+ # Vector DB Configuration
69
  QDRANT_URL = os.getenv("QDRANT_URL", "http://localhost:6333")
70
  QDRANT_API_KEY = os.getenv("QDRANT_API_KEY")
71
  COLLECTION_NAME = os.getenv("COLLECTION_NAME", "documents")
72
+
73
+ # Embedding Configuration
74
  EMBEDDING_MODEL = os.getenv("EMBEDDING_MODEL", "sentence-transformers/all-MiniLM-L6-v2")
75
+ TOP_K = int(os.getenv("TOP_K", "10"))
76
+ SIMILARITY_THRESHOLD = float(os.getenv("SIMILARITY_THRESHOLD", "0.1"))
77
  DEVICE = os.getenv("DEVICE", "cuda" if torch.cuda.is_available() else "cpu")
78
 
79
+ # Models
80
+ OPENROUTER_MODELS = ["deepseek/deepseek-chat-v3-0324:free", "deepseek/deepseek-r1-0528:free", "qwen/qwen3-235b-a22b:free", "google/gemini-2.0-flash-exp:free"]
81
+ GROQ_MODELS = ["llama-3.3-70b-versatile"]
82
+
83
+ class DynamicOpenAIService:
84
+ """Service for dynamic OpenAI provider selection"""
85
+
86
+ def __init__(self):
87
+ self.validate_api_keys()
88
+
89
+ def validate_api_keys(self):
90
+ """Validate that at least one API key is available"""
91
+ if not Config.OPENROUTER_API_KEY and not Config.GROQ_API_KEY:
92
+ raise ValueError("At least one API key (OPENROUTER_API_KEY or GROQ_API_KEY) must be provided")
93
+
94
+ if not Config.OPENROUTER_API_KEY:
95
+ print("Warning: OPENROUTER_API_KEY not found, will only use Groq")
96
+ if not Config.GROQ_API_KEY:
97
+ print("Warning: GROQ_API_KEY not found, will only use OpenRouter")
98
+
99
+ def get_client(self, provider="random"):
100
+ """Get OpenAI client for specified provider"""
101
+ available_providers = []
102
+
103
+ if Config.OPENROUTER_API_KEY:
104
+ available_providers.append("openrouter")
105
+ if Config.GROQ_API_KEY:
106
+ available_providers.append("groq")
107
+
108
+ if not available_providers:
109
+ raise ValueError("No API keys available for any provider")
110
+
111
+ if provider == "random":
112
+ provider = random.choice(available_providers)
113
+ elif provider not in available_providers:
114
+ # Fallback to available provider
115
+ provider = available_providers[0]
116
+ print(f"Requested provider not available, using {provider}")
117
+
118
+ print(f"Selected provider: {provider}")
119
+
120
+ if provider == "openrouter":
121
+ return (
122
+ OpenAI(api_key=Config.OPENROUTER_API_KEY, base_url="https://openrouter.ai/api/v1"),
123
+ OPENROUTER_MODELS,
124
+ provider
125
+ )
126
+ else: # groq
127
+ return (
128
+ OpenAI(api_key=Config.GROQ_API_KEY, base_url="https://api.groq.com/openai/v1"),
129
+ GROQ_MODELS,
130
+ provider
131
+ )
132
+
133
+ async def get_async_client(self, provider="random"):
134
+ """Get AsyncOpenAI client for specified provider"""
135
+ available_providers = []
136
+
137
+ if Config.OPENROUTER_API_KEY:
138
+ available_providers.append("openrouter")
139
+ if Config.GROQ_API_KEY:
140
+ available_providers.append("groq")
141
+
142
+ if not available_providers:
143
+ raise ValueError("No API keys available for any provider")
144
+
145
+ if provider == "random":
146
+ provider = random.choice(available_providers)
147
+ elif provider not in available_providers:
148
+ # Fallback to available provider
149
+ provider = available_providers[0]
150
+ print(f"Requested provider not available, using {provider}")
151
+
152
+ print(f"Selected provider: {provider}")
153
+
154
+ if provider == "openrouter":
155
+ return (
156
+ AsyncOpenAI(api_key=Config.OPENROUTER_API_KEY, base_url="https://openrouter.ai/api/v1"),
157
+ OPENROUTER_MODELS,
158
+ provider
159
+ )
160
+ else: # groq
161
+ return (
162
+ AsyncOpenAI(api_key=Config.GROQ_API_KEY, base_url="https://api.groq.com/openai/v1"),
163
+ GROQ_MODELS,
164
+ provider
165
+ )
166
+
167
+ def get_text_response(self, prompt, provider="random", model=None):
168
+ """Get text response from AI"""
169
+ client, models, selected_provider = self.get_client(provider)
170
+
171
+ if not model or model == "auto":
172
+ model = random.choice(models)
173
+
174
+ print(f"Using model: {model}")
175
+
176
+ response = client.chat.completions.create(
177
+ model=model,
178
+ messages=[{"role": "user", "content": prompt}],
179
+ max_tokens=1024,
180
+ temperature=0.7
181
+ )
182
+
183
+ return response.choices[0].message.content
184
+
185
+ def get_text_response_streaming(self, prompt, provider="random", model=None):
186
+ """Get streaming text response from AI"""
187
+ client, models, selected_provider = self.get_client(provider)
188
+
189
+ if not model or model == "auto":
190
+ model = random.choice(models)
191
+
192
+ print(f"Using model: {model}")
193
+
194
+ stream = client.chat.completions.create(
195
+ model=model,
196
+ messages=[{"role": "user", "content": prompt}],
197
+ max_tokens=1024,
198
+ temperature=0.7,
199
+ stream=True
200
+ )
201
+
202
+ for chunk in stream:
203
+ if chunk.choices[0].delta.content is not None:
204
+ yield chunk.choices[0].delta.content
205
+
206
  class ApplicationState:
207
  """Application state container"""
208
  def __init__(self):
209
+ self.openai_service = None
210
  self.qdrant_client = None
211
  self.embedding_service = None
212
  self.document_manager = None
 
555
  return []
556
 
557
  # Use a lower similarity threshold for RAG to get more results
 
558
  min_score = 0.1 # Lower threshold for RAG
559
 
560
  print(f"RAG Search - Query: '{query}', Limit: {top_k}, Min Score: {min_score}")
 
590
  if not results:
591
  return query
592
 
593
+ # Build context parts
594
  context_parts = []
595
  for result in results:
596
  context_parts.append(f"Source: {result['file_path']}\n{result['text']}")
 
611
  @asynccontextmanager
612
  async def lifespan(app: FastAPI):
613
  # Startup
 
 
 
614
  print("Initializing services...")
615
 
616
+ # Initialize dynamic OpenAI service
617
  try:
618
+ app_state.openai_service = DynamicOpenAIService()
619
+ print(" Dynamic OpenAI service initialized")
 
 
 
 
 
 
 
 
620
  except Exception as e:
621
+ print(f"✗ Error initializing OpenAI service: {e}")
622
  raise e
623
 
624
  # Initialize Qdrant client
 
663
  if app_state.qdrant_client:
664
  await app_state.qdrant_client.close()
665
  print("✓ Qdrant client closed")
 
 
 
666
  if app_state.embedding_service and hasattr(app_state.embedding_service, 'executor'):
667
  app_state.embedding_service.executor.shutdown(wait=True)
668
  print("✓ Embedding service executor shutdown")
 
673
 
674
  # Initialize FastAPI app
675
  app = FastAPI(
676
+ title="Enhanced RAG API with Dynamic Provider Selection",
677
+ description="OpenAI-compatible API for RAG with dynamic provider selection (OpenRouter/Groq) and document management",
678
  version="1.0.0",
679
  lifespan=lifespan
680
  )
681
 
682
  @app.get("/")
683
  async def root():
684
+ return {"message": "Enhanced RAG API with Dynamic Provider Selection", "status": "running"}
685
 
686
  @app.get("/health")
687
  async def health_check():
 
705
  except Exception as e:
706
  embedding_health = {"status": "error", "error": str(e)}
707
 
708
+ # Test OpenAI service
709
+ if app_state.openai_service is None:
710
+ openai_health = {"status": "not_initialized", "error": "OpenAI service is None"}
711
  else:
712
  try:
713
+ # Test both providers if available
714
+ test_results = {}
715
+ if Config.OPENROUTER_API_KEY:
716
+ try:
717
+ client, models, provider = app_state.openai_service.get_client("openrouter")
718
+ test_response = client.chat.completions.create(
719
+ model=models[0],
720
+ messages=[{"role": "user", "content": "test"}],
721
+ max_tokens=1
722
+ )
723
+ test_results["openrouter"] = {"status": "healthy", "model": models[0]}
724
+ except Exception as e:
725
+ test_results["openrouter"] = {"status": "error", "error": str(e)}
726
+
727
+ if Config.GROQ_API_KEY:
728
+ try:
729
+ client, models, provider = app_state.openai_service.get_client("groq")
730
+ test_response = client.chat.completions.create(
731
+ model=models[0],
732
+ messages=[{"role": "user", "content": "test"}],
733
+ max_tokens=1
734
+ )
735
+ test_results["groq"] = {"status": "healthy", "model": models[0]}
736
+ except Exception as e:
737
+ test_results["groq"] = {"status": "error", "error": str(e)}
738
+
739
+ openai_health = {"status": "healthy", "providers": test_results}
740
  except Exception as e:
741
  openai_health = {"status": "error", "error": str(e)}
742
 
743
  return {
744
  "status": "healthy" if app_state.embedding_service is not None else "unhealthy",
745
+ "openai_service": openai_health,
746
  "qdrant": qdrant_status,
747
  "embedding_service": embedding_health,
748
  "document_manager": "initialized" if app_state.document_manager else "not_initialized",
749
  "collection": Config.COLLECTION_NAME,
750
  "embedding_model": Config.EMBEDDING_MODEL,
751
+ "available_providers": {
752
+ "openrouter": bool(Config.OPENROUTER_API_KEY),
753
+ "groq": bool(Config.GROQ_API_KEY)
754
+ }
755
  }
756
 
757
  @app.post("/v1/chat/completions")
758
  async def chat_completions(request: ChatCompletionRequest):
759
+ """OpenAI-compatible chat completions endpoint with enhanced RAG and dynamic provider selection"""
760
 
761
+ if not app_state.openai_service:
762
+ raise HTTPException(status_code=500, detail="OpenAI service not initialized")
763
 
764
  try:
765
  # Get the last user message for retrieval
 
808
  raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
809
 
810
  async def create_chat_completion(messages: List[Dict], request: ChatCompletionRequest) -> ChatCompletionResponse:
811
+ """Create a non-streaming chat completion using dynamic provider selection"""
812
  try:
813
+ # Get async client with dynamic provider selection
814
+ client, models, selected_provider = await app_state.openai_service.get_async_client(request.provider)
815
+
816
+ # Select model
817
+ if request.model == "auto" or not request.model:
818
+ selected_model = random.choice(models)
819
+ else:
820
+ selected_model = request.model
821
+
822
+ print(f"Using provider: {selected_provider}, model: {selected_model}")
823
+
824
+ response = await client.chat.completions.create(
825
+ model=selected_model,
826
  messages=messages,
827
  max_tokens=request.max_tokens,
828
  temperature=request.temperature,
 
833
  result = ChatCompletionResponse(
834
  id=response.id,
835
  created=response.created,
836
+ model=f"{selected_provider}:{response.model}", # Include provider in model name
837
  choices=[{
838
  "index": choice.index,
839
  "message": {
 
856
  raise HTTPException(status_code=500, detail=f"Error calling OpenAI API: {str(e)}")
857
 
858
  async def stream_chat_completion(messages: List[Dict], request: ChatCompletionRequest) -> AsyncGenerator[str, None]:
859
+ """Stream chat completion responses using dynamic provider selection"""
860
  try:
861
+ # Get async client with dynamic provider selection
862
+ client, models, selected_provider = await app_state.openai_service.get_async_client(request.provider)
863
+
864
+ # Select model
865
+ if request.model == "auto" or not request.model:
866
+ selected_model = random.choice(models)
867
+ else:
868
+ selected_model = request.model
869
+
870
+ print(f"Using provider: {selected_provider}, model: {selected_model}")
871
+
872
+ stream = await client.chat.completions.create(
873
+ model=selected_model,
874
  messages=messages,
875
  max_tokens=request.max_tokens,
876
  temperature=request.temperature,
 
885
  chunk_response = ChatCompletionChunk(
886
  id=chunk.id,
887
  created=chunk.created,
888
+ model=f"{selected_provider}:{chunk.model}", # Include provider in model name
889
  choices=[{
890
  "index": choice.index,
891
  "delta": {
 
1077
  except Exception as e:
1078
  raise HTTPException(status_code=500, detail=f"Error getting collection info: {str(e)}")
1079
 
1080
+ # New endpoint to get available providers and models
1081
+ @app.get("/v1/providers")
1082
+ async def get_providers():
1083
+ """Get available providers and their models"""
1084
+ try:
1085
+ if not app_state.openai_service:
1086
+ raise HTTPException(status_code=500, detail="OpenAI service not initialized")
1087
+
1088
+ available_providers = {}
1089
+
1090
+ if Config.OPENROUTER_API_KEY:
1091
+ available_providers["openrouter"] = {
1092
+ "status": "available",
1093
+ "models": OPENROUTER_MODELS
1094
+ }
1095
+ else:
1096
+ available_providers["openrouter"] = {
1097
+ "status": "unavailable",
1098
+ "reason": "API key not provided"
1099
+ }
1100
+
1101
+ if Config.GROQ_API_KEY:
1102
+ available_providers["groq"] = {
1103
+ "status": "available",
1104
+ "models": GROQ_MODELS
1105
+ }
1106
+ else:
1107
+ available_providers["groq"] = {
1108
+ "status": "unavailable",
1109
+ "reason": "API key not provided"
1110
+ }
1111
+
1112
+ return {
1113
+ "providers": available_providers,
1114
+ "default_selection": "random"
1115
+ }
1116
+
1117
+ except Exception as e:
1118
+ raise HTTPException(status_code=500, detail=f"Error getting providers: {str(e)}")
1119
+
1120
  if __name__ == "__main__":
1121
  import uvicorn
1122
  uvicorn.run(app, host="0.0.0.0", port=8000)