chore: format, improve logging, remove deprecated code
Browse files- .dockerignore +0 -1
- app/agent/chat_agent_client.py +1 -1
- app/core/api_response.py +5 -42
- app/mapper/chat_mapper.py +1 -1
- app/model/chat_model.py +0 -45
- app/repository/chat_repository.py +1 -1
- app/schema/chat_schema.py +1 -2
- app/security/auth_service.py +1 -3
- app/service/chat_service.py +3 -3
- gradio_chatbot.py +1 -1
- main.py +1 -1
- resources/mock/message.md +7 -7
- run.sh +5 -5
- scripts/api_key_genenerator.py +1 -2
.dockerignore
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
.git/
|
| 2 |
.github/
|
| 3 |
-
.vscode/
|
| 4 |
.venv/
|
| 5 |
__pycache__/
|
| 6 |
dist/
|
|
|
|
| 1 |
.git/
|
| 2 |
.github/
|
|
|
|
| 3 |
.venv/
|
| 4 |
__pycache__/
|
| 5 |
dist/
|
app/agent/chat_agent_client.py
CHANGED
|
@@ -9,6 +9,6 @@ class ChatAgentClient:
|
|
| 9 |
# TODO implement the logic to process the chat
|
| 10 |
agent_name = self.agent_name
|
| 11 |
return AssistantChatAgentResponse(
|
| 12 |
-
message=f"Here is the {agent_name} Processed message: This is a placeholder response for the user-question",
|
| 13 |
figure=None, # Placeholder for any figure data if needed
|
| 14 |
)
|
|
|
|
| 9 |
# TODO implement the logic to process the chat
|
| 10 |
agent_name = self.agent_name
|
| 11 |
return AssistantChatAgentResponse(
|
| 12 |
+
message=f"Here is the {agent_name} Processed message: This is a placeholder response for the user-question typeOfTheRequest:{type(user_chat_agent_request)}",
|
| 13 |
figure=None, # Placeholder for any figure data if needed
|
| 14 |
)
|
app/core/api_response.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
from functools import wraps
|
| 2 |
-
import
|
| 3 |
from fastapi import HTTPException, Request, status
|
| 4 |
from loguru import logger
|
| 5 |
import os
|
|
@@ -10,55 +10,18 @@ from environs import Env
|
|
| 10 |
env = Env()
|
| 11 |
env.read_env()
|
| 12 |
|
| 13 |
-
USE_MOCK = env.bool("USE_MOCK",
|
| 14 |
MOCK_DIR = env.str("MOCK_DIR", "resources/mock")
|
| 15 |
|
| 16 |
# Deprecated code. because we are using mongomock-motor for database_type=embedded
|
| 17 |
-
# TODO: remove this code after we switch to real database
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
def url_to_filename(url: str, method: str) -> str:
|
| 21 |
-
"""
|
| 22 |
-
Convert API URL to mock filename.
|
| 23 |
-
Example:
|
| 24 |
-
- Input: GET "/v1/chat/completions" -> "chat_completions_GET.json"
|
| 25 |
-
- Input: GET "/v1/chat/completions/{completion_id}" -> "chat_completions_id_GET.json"
|
| 26 |
-
- Input: GET "/v1/chat/completions/123/messages" -> "chat_completions_id_messages_GET.json"
|
| 27 |
-
"""
|
| 28 |
-
logger.trace(f"BEGIN: url: {url} method: {method}")
|
| 29 |
-
# Remove version prefix and leading/trailing slashes
|
| 30 |
-
path = url.strip("/")
|
| 31 |
-
if path.startswith("v1/"):
|
| 32 |
-
path = path[3:]
|
| 33 |
-
|
| 34 |
-
# Replace path parameters with descriptive names
|
| 35 |
-
path = path.replace("{completion_id}", "id")
|
| 36 |
-
path = path.replace("{message_id}", "id")
|
| 37 |
-
|
| 38 |
-
logger.trace(f"replaced path: {path}")
|
| 39 |
-
|
| 40 |
-
# Convert to filename format
|
| 41 |
-
filename = path.replace("/", "_")
|
| 42 |
-
logger.trace(f"filename: {filename}")
|
| 43 |
-
|
| 44 |
-
# convert conversations_1 to conversations with dynamic id
|
| 45 |
-
final_filename = re.sub(r"[_][^/]+$", "_id", filename)
|
| 46 |
-
logger.trace(f"final_filename: {final_filename}")
|
| 47 |
-
|
| 48 |
-
# Add method suffix
|
| 49 |
-
result = f"{final_filename}_{method}"
|
| 50 |
-
logger.trace(f"END: result: {result}")
|
| 51 |
-
return result
|
| 52 |
|
| 53 |
|
|
|
|
| 54 |
def get_mock_response(url_path: str, python_module_name: str, python_method_name: str) -> Dict:
|
| 55 |
"""Get mock response from JSON file."""
|
| 56 |
logger.trace(f"BEGIN: url_path: {url_path} python_module_name: {python_module_name} python_method_name: {python_method_name}")
|
| 57 |
-
filename = None
|
| 58 |
file_path = None
|
| 59 |
try:
|
| 60 |
-
# Convert to filename
|
| 61 |
-
# filename = url_to_filename(url_path, method)
|
| 62 |
filename = python_module_name + "_" + python_method_name
|
| 63 |
filename = filename.replace(".", "_")
|
| 64 |
filename = filename.replace("__", "_")
|
|
@@ -67,7 +30,7 @@ def get_mock_response(url_path: str, python_module_name: str, python_method_name
|
|
| 67 |
filename = filename.replace(" ", "_")
|
| 68 |
filename = filename.replace("-", "_")
|
| 69 |
filename = filename.replace("app_api_", "")
|
| 70 |
-
|
| 71 |
# Load mock response
|
| 72 |
file_path = os.path.join(MOCK_DIR, f"{filename}.json")
|
| 73 |
|
|
@@ -89,7 +52,7 @@ def get_mock_response(url_path: str, python_module_name: str, python_method_name
|
|
| 89 |
|
| 90 |
|
| 91 |
def api_response():
|
| 92 |
-
"""Decorator to handle
|
| 93 |
|
| 94 |
def decorator(func):
|
| 95 |
logger.trace(f"BEGIN: decorator: {func}")
|
|
|
|
| 1 |
from functools import wraps
|
| 2 |
+
from warnings import deprecated
|
| 3 |
from fastapi import HTTPException, Request, status
|
| 4 |
from loguru import logger
|
| 5 |
import os
|
|
|
|
| 10 |
env = Env()
|
| 11 |
env.read_env()
|
| 12 |
|
| 13 |
+
USE_MOCK = env.bool("USE_MOCK", False)
|
| 14 |
MOCK_DIR = env.str("MOCK_DIR", "resources/mock")
|
| 15 |
|
| 16 |
# Deprecated code. because we are using mongomock-motor for database_type=embedded
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
|
| 19 |
+
@deprecated("This function is deprecated. Use database_type=embedded with mongomock-motor instead.")
|
| 20 |
def get_mock_response(url_path: str, python_module_name: str, python_method_name: str) -> Dict:
|
| 21 |
"""Get mock response from JSON file."""
|
| 22 |
logger.trace(f"BEGIN: url_path: {url_path} python_module_name: {python_module_name} python_method_name: {python_method_name}")
|
|
|
|
| 23 |
file_path = None
|
| 24 |
try:
|
|
|
|
|
|
|
| 25 |
filename = python_module_name + "_" + python_method_name
|
| 26 |
filename = filename.replace(".", "_")
|
| 27 |
filename = filename.replace("__", "_")
|
|
|
|
| 30 |
filename = filename.replace(" ", "_")
|
| 31 |
filename = filename.replace("-", "_")
|
| 32 |
filename = filename.replace("app_api_", "")
|
| 33 |
+
logger.trace(f"FileName : {filename}")
|
| 34 |
# Load mock response
|
| 35 |
file_path = os.path.join(MOCK_DIR, f"{filename}.json")
|
| 36 |
|
|
|
|
| 52 |
|
| 53 |
|
| 54 |
def api_response():
|
| 55 |
+
"""Decorator to handle API request and response"""
|
| 56 |
|
| 57 |
def decorator(func):
|
| 58 |
logger.trace(f"BEGIN: decorator: {func}")
|
app/mapper/chat_mapper.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
import datetime
|
| 2 |
from app.mapper.base_mapper import BaseMapper
|
| 3 |
from app.model.chat_model import ChatCompletion, ChatMessageModel
|
| 4 |
from app.schema.chat_schema import ChatCompletionResponse, ChatCompletionRequest, ChatMessageResponse, ChoiceResponse
|
|
|
|
| 1 |
+
from datetime import datetime
|
| 2 |
from app.mapper.base_mapper import BaseMapper
|
| 3 |
from app.model.chat_model import ChatCompletion, ChatMessageModel
|
| 4 |
from app.schema.chat_schema import ChatCompletionResponse, ChatCompletionRequest, ChatMessageResponse, ChoiceResponse
|
app/model/chat_model.py
CHANGED
|
@@ -5,51 +5,6 @@ from pydantic import BaseModel, Field
|
|
| 5 |
from datetime import datetime
|
| 6 |
from typing import List, Optional, Any
|
| 7 |
|
| 8 |
-
# Chat completion payload example
|
| 9 |
-
# {
|
| 10 |
-
# "model": "gpt-4o",
|
| 11 |
-
# "messages": [
|
| 12 |
-
# {
|
| 13 |
-
# "role": "system",
|
| 14 |
-
# "content": "You are a travel assistant with expertise in creating personalized travel itineraries. Provide detailed, practical, and concise recommendations, including specific locations, activities, and tips based on the user's preferences. Always consider budget, travel dates, and any special requests."
|
| 15 |
-
# },
|
| 16 |
-
# {
|
| 17 |
-
# "role": "user",
|
| 18 |
-
# "content": "I'm planning a 5-day trip to Italy in June 2025 with a moderate budget. I love history, food, and scenic views. Where should I go, and what should I do?"
|
| 19 |
-
# },
|
| 20 |
-
# {
|
| 21 |
-
# "role": "assistant",
|
| 22 |
-
# "content": "For a 5-day trip to Italy with a focus on history, food, and scenic views on a moderate budget, I recommend visiting Rome and Florence. Here's a suggested itinerary:\n\n**Day 1-3: Rome**\n- **Day 1**: Visit the Colosseum (book skip-the-line tickets online for ~€16) and Roman Forum for a dive into ancient history. Enjoy dinner at a trattoria in Trastevere, like Osteria da Zi Umberto (~€15-20 per person).\n- **Day 2**: Explore the Pantheon (free entry) and Vatican Museums (Sistine Chapel, ~€20). Take an evening stroll at Piazza Navona. Try gelato at Gelateria del Teatro (~€3).\n- **Day 3**: Visit the Capitoline Museums (€15) and take a scenic walk along the Tiber River. Enjoy carbonara at Roscioli (~€12).\n\n**Day 4-5: Florence**\n- **Day 4**: Take a morning train from Rome to Florence (~€30, 1.5 hours). Visit the Uffizi Gallery (book ahead, ~€20) and stroll Ponte Vecchio for views. Dine at Trattoria Mario for affordable Tuscan dishes (~€10-15).\n- **Day 5**: Explore the Florence Cathedral (Duomo, free entry; dome climb ~€20) and enjoy views from Piazzale Michelangelo (free). Try a lampredotto sandwich at a local food stall (~€5).\n\n**Tips**: Book accommodations like budget hotels or Airbnb (~€60-100/night). Use regional trains for cost savings. June weather is warm, so pack light clothing and book tickets early to avoid crowds."
|
| 23 |
-
# },
|
| 24 |
-
# {
|
| 25 |
-
# "role": "user",
|
| 26 |
-
# "content": "That sounds great! Can you suggest a specific hotel in Rome and any food markets to visit in Florence?"
|
| 27 |
-
# },
|
| 28 |
-
# {
|
| 29 |
-
# "role": "assistant",
|
| 30 |
-
# "content": "**Rome Hotel**: Consider **Hotel Artemide**, a centrally located 4-star hotel with rates around €80-100 per night on a moderate budget. It's near Termini Station and within walking distance of major sites like the Trevi Fountain. Book directly for potential deals.\n\n**Florence Food Markets**:\n- **Mercato Centrale**: A vibrant indoor market with food stalls offering fresh pasta, pizza, and local cheeses. Try a plate of fresh tortellini (~€8) or focaccia. Open daily, great for lunch.\n- **Sant'Ambrogio Market**: Less touristy, with fresh produce and local specialties like porchetta sandwiches (~€5). It's perfect for an authentic experience, located near Santa Croce. Open mornings until 2 PM.\n\n**Tip**: In Florence, pair market visits with a stop at a nearby café for espresso (~€1.50) to soak in the local vibe."
|
| 31 |
-
# },
|
| 32 |
-
# {
|
| 33 |
-
# "role": "user",
|
| 34 |
-
# "content": "What about transportation between cities and within Florence?"
|
| 35 |
-
# },
|
| 36 |
-
# {
|
| 37 |
-
# "role": "assistant",
|
| 38 |
-
# "content": "**Between Cities**: Take a high-speed train (Italo or Trenitalia) from Rome to Florence. Book in advance for tickets around €25-35 one-way (1.5 hours). Trains depart from Roma Termini and arrive at Firenze Santa Maria Novella. Check schedules on trenitalia.com or italo.it.\n\n**Within Florence**: Florence is compact and walkable, so you won't need much public transport. For longer distances (e.g., to Piazzale Michelangelo), use ATAF buses (~€1.50 per ride, buy tickets at tabaccherie). Alternatively, rent a bike (~€10/day) for a scenic way to explore. Taxis are pricier (~€10-15 for short trips), so stick to walking or buses for budget travel.",
|
| 39 |
-
# "figure": {
|
| 40 |
-
# "data": {},
|
| 41 |
-
# "layout": {}
|
| 42 |
-
# }
|
| 43 |
-
# }
|
| 44 |
-
# ],
|
| 45 |
-
# "temperature": 0.8,
|
| 46 |
-
# "max_tokens": 500,
|
| 47 |
-
# "top_p": 1.0,
|
| 48 |
-
# "frequency_penalty": 0.2,
|
| 49 |
-
# "presence_penalty": 0.1
|
| 50 |
-
# "stream": false
|
| 51 |
-
# }
|
| 52 |
-
|
| 53 |
|
| 54 |
class ChatMessageModel(BaseModel):
|
| 55 |
"""
|
|
|
|
| 5 |
from datetime import datetime
|
| 6 |
from typing import List, Optional, Any
|
| 7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
class ChatMessageModel(BaseModel):
|
| 10 |
"""
|
app/repository/chat_repository.py
CHANGED
|
@@ -163,7 +163,7 @@ class ChatRepository:
|
|
| 163 |
return final_entity
|
| 164 |
except Exception as e:
|
| 165 |
logger.error(f"Error parsing ChatCompletion from DB for id {completion_id}: {e}", exc_info=True)
|
| 166 |
-
return None
|
| 167 |
else:
|
| 168 |
logger.info(f"Chat completion with ID {completion_id} not found in DB.")
|
| 169 |
return None
|
|
|
|
| 163 |
return final_entity
|
| 164 |
except Exception as e:
|
| 165 |
logger.error(f"Error parsing ChatCompletion from DB for id {completion_id}: {e}", exc_info=True)
|
| 166 |
+
return None
|
| 167 |
else:
|
| 168 |
logger.info(f"Chat completion with ID {completion_id} not found in DB.")
|
| 169 |
return None
|
app/schema/chat_schema.py
CHANGED
|
@@ -46,7 +46,6 @@ class ChoiceResponse(BaseModel):
|
|
| 46 |
)
|
| 47 |
index: Optional[int] = Field(None, description="The index of the choice in the list of choices.")
|
| 48 |
message: Optional[ChatMessageResponse] = Field(None, description="The message to use for the chat completion")
|
| 49 |
-
# logprobs: str = None # not implemented yet
|
| 50 |
|
| 51 |
|
| 52 |
class ChatCompletionResponse(BaseModel):
|
|
@@ -60,7 +59,7 @@ class ChatCompletionResponse(BaseModel):
|
|
| 60 |
model: Optional[str] = Field(None, description="The model used for the chat completion")
|
| 61 |
# not implemented yet
|
| 62 |
# service_tier: Optional[ServiceTier] = ServiceTier.AUTO
|
| 63 |
-
# system_fingerprint: Optional[StrictStr] = Field(default=None, description=
|
| 64 |
# object: StrictStr = Field(description="The object type, which is always `chat.completion`.")
|
| 65 |
# usage: Optional[CompletionUsage] = None
|
| 66 |
|
|
|
|
| 46 |
)
|
| 47 |
index: Optional[int] = Field(None, description="The index of the choice in the list of choices.")
|
| 48 |
message: Optional[ChatMessageResponse] = Field(None, description="The message to use for the chat completion")
|
|
|
|
| 49 |
|
| 50 |
|
| 51 |
class ChatCompletionResponse(BaseModel):
|
|
|
|
| 59 |
model: Optional[str] = Field(None, description="The model used for the chat completion")
|
| 60 |
# not implemented yet
|
| 61 |
# service_tier: Optional[ServiceTier] = ServiceTier.AUTO
|
| 62 |
+
# system_fingerprint: Optional[StrictStr] = Field(default=None, description=This fingerprint represents the backend configuration that the model runs with. Can be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.)
|
| 63 |
# object: StrictStr = Field(description="The object type, which is always `chat.completion`.")
|
| 64 |
# usage: Optional[CompletionUsage] = None
|
| 65 |
|
app/security/auth_service.py
CHANGED
|
@@ -12,7 +12,7 @@ api_key_header = APIKeyHeader(
|
|
| 12 |
name="Authorization",
|
| 13 |
scheme_name="ApiKeyAuth",
|
| 14 |
description="API key in the format: sk-{username}-{base64_encoded_data}",
|
| 15 |
-
auto_error=False,
|
| 16 |
)
|
| 17 |
|
| 18 |
|
|
@@ -51,8 +51,6 @@ class AuthService:
|
|
| 51 |
status_code=status.HTTP_401_UNAUTHORIZED,
|
| 52 |
detail=f"Invalid API key data format: {str(e)}",
|
| 53 |
)
|
| 54 |
-
|
| 55 |
-
# Debug için JSON verilerini logla
|
| 56 |
json_data = {
|
| 57 |
"username": data["username"],
|
| 58 |
"created_at": data["created_at"],
|
|
|
|
| 12 |
name="Authorization",
|
| 13 |
scheme_name="ApiKeyAuth",
|
| 14 |
description="API key in the format: sk-{username}-{base64_encoded_data}",
|
| 15 |
+
auto_error=False,
|
| 16 |
)
|
| 17 |
|
| 18 |
|
|
|
|
| 51 |
status_code=status.HTTP_401_UNAUTHORIZED,
|
| 52 |
detail=f"Invalid API key data format: {str(e)}",
|
| 53 |
)
|
|
|
|
|
|
|
| 54 |
json_data = {
|
| 55 |
"username": data["username"],
|
| 56 |
"created_at": data["created_at"],
|
app/service/chat_service.py
CHANGED
|
@@ -124,12 +124,12 @@ class ChatService:
|
|
| 124 |
else:
|
| 125 |
logger.info("Update existing chat completion with new user request message")
|
| 126 |
|
| 127 |
-
logger.
|
| 128 |
current_db_entity.messages.append(last_user_message_model)
|
| 129 |
-
logger.
|
| 130 |
current_db_entity.last_updated_date = datetime.datetime.now()
|
| 131 |
final_entity = await self.chat_repository.update(current_db_entity)
|
| 132 |
-
logger.
|
| 133 |
|
| 134 |
# Convert model to response
|
| 135 |
result = self.chat_mapper.to_schema(final_entity, convert_last_message=True)
|
|
|
|
| 124 |
else:
|
| 125 |
logger.info("Update existing chat completion with new user request message")
|
| 126 |
|
| 127 |
+
logger.trace(f"before update. current db entity messages count: {len(current_db_entity.messages)}")
|
| 128 |
current_db_entity.messages.append(last_user_message_model)
|
| 129 |
+
logger.trace(f"after update. current db entity messages count: {len(current_db_entity.messages)}")
|
| 130 |
current_db_entity.last_updated_date = datetime.datetime.now()
|
| 131 |
final_entity = await self.chat_repository.update(current_db_entity)
|
| 132 |
+
logger.trace(f"after update. final entity messages count: {len(final_entity.messages)}")
|
| 133 |
|
| 134 |
# Convert model to response
|
| 135 |
result = self.chat_mapper.to_schema(final_entity, convert_last_message=True)
|
gradio_chatbot.py
CHANGED
|
@@ -13,7 +13,7 @@ import plotly.graph_objects as go
|
|
| 13 |
env = environs.Env()
|
| 14 |
env.read_env()
|
| 15 |
|
| 16 |
-
#
|
| 17 |
IS_HF_SPACE = os.environ.get("SPACE_ID") is not None
|
| 18 |
SPACE_URL = "https://lokumai-openai-openapi-template.hf.space" if IS_HF_SPACE else "http://localhost:7860"
|
| 19 |
|
|
|
|
| 13 |
env = environs.Env()
|
| 14 |
env.read_env()
|
| 15 |
|
| 16 |
+
# special handling for Hugging Face Space
|
| 17 |
IS_HF_SPACE = os.environ.get("SPACE_ID") is not None
|
| 18 |
SPACE_URL = "https://lokumai-openai-openapi-template.hf.space" if IS_HF_SPACE else "http://localhost:7860"
|
| 19 |
|
main.py
CHANGED
|
@@ -21,7 +21,7 @@ env.read_env()
|
|
| 21 |
|
| 22 |
DB_DATABASE_TYPE = env.str("DB_DATABASE_TYPE", "mongodb")
|
| 23 |
|
| 24 |
-
#
|
| 25 |
IS_HF_SPACE = os.environ.get("SPACE_ID") is not None
|
| 26 |
SPACE_URL = "https://lokumai-openai-openapi-template.hf.space" if IS_HF_SPACE else "http://localhost:7860"
|
| 27 |
|
|
|
|
| 21 |
|
| 22 |
DB_DATABASE_TYPE = env.str("DB_DATABASE_TYPE", "mongodb")
|
| 23 |
|
| 24 |
+
# special handling for Hugging Face Space
|
| 25 |
IS_HF_SPACE = os.environ.get("SPACE_ID") is not None
|
| 26 |
SPACE_URL = "https://lokumai-openai-openapi-template.hf.space" if IS_HF_SPACE else "http://localhost:7860"
|
| 27 |
|
resources/mock/message.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
| 1 |
## Result
|
| 2 |
-
|status|count|
|
| 3 |
-
|
| 4 |
-
|active|100|
|
| 5 |
-
|cancelled|30|
|
| 6 |
-
|pending|40|
|
| 7 |
-
|passive|150|
|
| 8 |
|
| 9 |
## Explanation
|
| 10 |
This result represents the number of active, cancelled, pending and passive customers in the year 2025. This is a mock result for the question "How many customers are active, cancelled, pending and passive in the year 2025?"
|
| 11 |
|
| 12 |
## Query
|
| 13 |
-
|
| 14 |
SELECT status, COUNT(*) FROM customers WHERE year = 2025 GROUP BY status;
|
| 15 |
```
|
|
|
|
| 1 |
## Result
|
| 2 |
+
| status | count |
|
| 3 |
+
|-----------|-------|
|
| 4 |
+
| active | 100 |
|
| 5 |
+
| cancelled | 30 |
|
| 6 |
+
| pending | 40 |
|
| 7 |
+
| passive | 150 |
|
| 8 |
|
| 9 |
## Explanation
|
| 10 |
This result represents the number of active, cancelled, pending and passive customers in the year 2025. This is a mock result for the question "How many customers are active, cancelled, pending and passive in the year 2025?"
|
| 11 |
|
| 12 |
## Query
|
| 13 |
+
``sql
|
| 14 |
SELECT status, COUNT(*) FROM customers WHERE year = 2025 GROUP BY status;
|
| 15 |
```
|
run.sh
CHANGED
|
@@ -1,18 +1,18 @@
|
|
| 1 |
#!/bin/bash
|
| 2 |
|
| 3 |
-
#
|
| 4 |
echo "Stopping existing application..."
|
| 5 |
pkill -f "uvicorn main:app"
|
| 6 |
|
| 7 |
-
#
|
| 8 |
sleep 2
|
| 9 |
|
| 10 |
-
# FastAPI
|
| 11 |
echo "Starting application..."
|
| 12 |
uv run uvicorn main:app --host 0.0.0.0 --port 7860 --reload
|
| 13 |
|
| 14 |
-
# Ctrl+C
|
| 15 |
trap "echo 'Stopping application...'; pkill -f 'uvicorn main:app'; exit" INT
|
| 16 |
|
| 17 |
-
#
|
| 18 |
wait
|
|
|
|
| 1 |
#!/bin/bash
|
| 2 |
|
| 3 |
+
# stop existing application if running
|
| 4 |
echo "Stopping existing application..."
|
| 5 |
pkill -f "uvicorn main:app"
|
| 6 |
|
| 7 |
+
# wait for the process to stop
|
| 8 |
sleep 2
|
| 9 |
|
| 10 |
+
# start FastAPI application with uvicorn integrated Gradio Application
|
| 11 |
echo "Starting application..."
|
| 12 |
uv run uvicorn main:app --host 0.0.0.0 --port 7860 --reload
|
| 13 |
|
| 14 |
+
# catch SIGINT (Ctrl+C) to stop the application gracefully
|
| 15 |
trap "echo 'Stopping application...'; pkill -f 'uvicorn main:app'; exit" INT
|
| 16 |
|
| 17 |
+
# wait for the application to run
|
| 18 |
wait
|
scripts/api_key_genenerator.py
CHANGED
|
@@ -24,7 +24,7 @@ from loguru import logger
|
|
| 24 |
logger.add("logs/api_key_generator.log")
|
| 25 |
|
| 26 |
|
| 27 |
-
def generate_api_key(username: str, secret_key: str) -> str:
|
| 28 |
"""
|
| 29 |
Generates an API key for the given username using a secret key.
|
| 30 |
|
|
@@ -39,7 +39,6 @@ def generate_api_key(username: str, secret_key: str) -> str:
|
|
| 39 |
timestamp = int(datetime.now().timestamp())
|
| 40 |
data = {"username": username, "created_at": timestamp}
|
| 41 |
|
| 42 |
-
# Debug için JSON verilerini logla
|
| 43 |
json_data = {"username": username, "created_at": timestamp}
|
| 44 |
json_str = json.dumps(json_data)
|
| 45 |
logger.debug(f"JSON data for signature: {json_str}")
|
|
|
|
| 24 |
logger.add("logs/api_key_generator.log")
|
| 25 |
|
| 26 |
|
| 27 |
+
def generate_api_key(username: str, secret_key: str) -> tuple[str, int]:
|
| 28 |
"""
|
| 29 |
Generates an API key for the given username using a secret key.
|
| 30 |
|
|
|
|
| 39 |
timestamp = int(datetime.now().timestamp())
|
| 40 |
data = {"username": username, "created_at": timestamp}
|
| 41 |
|
|
|
|
| 42 |
json_data = {"username": username, "created_at": timestamp}
|
| 43 |
json_str = json.dumps(json_data)
|
| 44 |
logger.debug(f"JSON data for signature: {json_str}")
|