You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

304 lines
11 KiB
Python

from datetime import datetime
from typing import List, Optional
from uuid import UUID
from enum import Enum
from pydantic import Field, field_validator, BaseModel
from fastapi import UploadFile
from src.models import DefaultBase, Pagination
equipment_keywords = ["boiler", "turbine", "generator", "condenser", "pump", "valve", "motor", "transformer", "compressor", "fan", "heater", "cooler", "pipe", "tubes", "panel", "switchgear", "relay", "breaker", "bearing", "gearbox", "chiller", "evaporator", "conveyor", "filter", "separator", "reactor", "furnace", "burner", "nozzle", "rotor", "stator", "shaft", "seal", "coupling", "damper", "duct", "expander", "economizer", "superheater", "deaerator", "feedwater", "drum", "tank", "vessel", "silo", "hopper", "agitator", "mixer", "blower", "injector", "strainer", "screen", "scrubber", "stack", "chimney", "insulator", "busbar", "rectifier", "inverter", "panel", "switch", "sensor", "transmitter", "controller", "PLC", "DCS", "SCADA", "soot_blower", "economizer"]
class FlowType(str, Enum):
LLM_ONLY = "llm_only"
ORIGINAL = "rag_retrieval_first"
LLM_FIRST = "rag_llm_first"
BATCH_SUMMARIZE = "rag_batch_summarize"
AGENTIC = "rag_agentic"
class ChatMessageBase(DefaultBase):
role: str = Field(..., description="Role of the message sender (e.g., 'user', 'assistant')")
content: str = Field(..., description="Content of the chat message")
created_at: Optional[datetime] = Field(None, nullable=True)
updated_at: Optional[datetime] = Field(None, nullable=True)
created_by: Optional[str] = Field(None, nullable=True)
updated_by: Optional[str] = Field(None, nullable=True)
class ChatMessageCreate(ChatMessageBase):
pass
class ChatMessageUpdate(ChatMessageBase):
pass
class ChatMessageRead(ChatMessageBase):
id: UUID = Field(..., description="Unique identifier for the chat message")
class ChatMessagePagination(Pagination):
items: List[ChatMessageRead] = Field([], description="List of chat messages with pagination support")
class ChatMessageRead(ChatMessageBase):
id: UUID = Field(..., description="Unique identifier for the chat message")
class ChatMessagePagination(Pagination):
items: List[ChatMessageRead] = Field([], description="List of chat messages with pagination support")
class FileAttachment(DefaultBase):
filename: str
url: str
size: Optional[int] = None
content_type: Optional[str] = None
class ChatMessage(DefaultBase):
role: str
content: str
attachments: Optional[List[FileAttachment]] = Field(default_factory=list, description="Optional list of attached files")
class ChatRequest(DefaultBase):
conversation_id: Optional[UUID] = None
message_id: Optional[UUID] = None
message: Optional[str] = None
equipment_id: Optional[UUID] = None
attachments: Optional[List[UploadFile]] = None
flow_type: Optional[FlowType] = Field(default=FlowType.ORIGINAL, description="Type of processing flow to use")
questions_batch: Optional[List[str]] = Field(default=None, description="List of questions for batch processing (only used with batch_summarize flow)")
model: Optional[str] = Field(default=None, description="LLM model name to use")
context_state: Optional[dict] = Field(default=None, description="Current context state (symptoms, failure modes, etc.)")
enable_background_analysis: Optional[bool] = Field(default=True, description="Toggle to enable/disable background RCFA analysis")
class Performance(DefaultBase):
prompt_tokens: Optional[int] = None
response_tokens: Optional[int] = None
tokens_used: Optional[int] = None
response_time: Optional[float] = None
response_length: Optional[int] = None
class ChatResponse(DefaultBase):
conversation_id: UUID
message: str
user_message: str
context: Optional[List] = None
attachments: Optional[List] = None
sources: Optional[List] = None
sources_filepath: Optional[List] = None
timestamp: str
response_timestamp: str
chat_model: str
time_taken: Optional[float] = None # float, seconds
prompt_tokens: Optional[float] = None
response_tokens: Optional[float] = None
tokens_used: Optional[float] = None
class ChatHistory(DefaultBase):
id: str
user_id: str
message: str
attachments: Optional[List] = None # Store as comma-separated text array
attachments_filepath: Optional[List] = None # Store as comma-separated text array
response: str
context: Optional[List] = None
sources: Optional[List] = None
sources_filepath: Optional[List] = None # Store as comma-separated text array
time_taken: Optional[float] = None
prompt_tokens: Optional[float] = None
response_tokens: Optional[float] = None
tokens_used: Optional[float] = None
timestamp: Optional[datetime] = None
response_timestamp: Optional[datetime] = None
conversation_id: str
chat_model: Optional[str] = None
embedding_model: Optional[str] = None
ocr_model: Optional[str] = None
feedback: Optional[str] = None
feedback_comment: Optional[str] = None
feedback_screenshot: Optional[str] = None
last_location_tag: Optional[str] = None
context_info: Optional[dict] = None
references: Optional[List[dict]] = None
updated_at: Optional[datetime] = None
created_at: Optional[datetime] = None
@field_validator('user_id', 'conversation_id', mode='before')
@classmethod
def convert_uuid_to_str(cls, v):
return str(v) if v else None
class ChatHistoryMetadata(BaseModel):
rcfa_phase: str
rcfa_state: dict
title: str
class ChatHistoryResponse(BaseModel):
messages: List[ChatHistory]
metadata: ChatHistoryMetadata
class Conversation(DefaultBase):
user_id: str
conversation_id: str
conversation_title: str
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
is_exported: bool = False
exported_at: Optional[datetime] = None
report_name: Optional[str] = None
report_content: Optional[str] = None
rcfa_phase: Optional[str] = None
rcfa_state: Optional[dict] = None
@field_validator('user_id', 'conversation_id', mode='before')
@classmethod
def convert_uuid_to_str(cls, v):
return str(v) if v else None
class ConversationExportUpdate(BaseModel):
is_exported: bool = True
report_name: str
report_content: str
class ConversationUpdate(DefaultBase):
conversation_title: str = Field(..., description="New title for the conversation")
class ConversationDataPagination(Pagination):
items: List[Conversation] = Field([], description="List of conversations with pagination support")
class IntentDetection(BaseModel):
intent: str = Field(
description="Identifikasi intensi dari masukkan pengguna dengan pendekatan 'Intent Classification'. Identifikasi masukkan pengguna berdasarkan dengan enum.",
enum=[
"DefinisiKegagalan",
"DefinisiEquipment",
"DeskripsiDokumen",
"Mendeskripsikan",
"JelaskanGambar",
"GejalaKegagalan",
"LokasiKegagalan",
"TindakanMitigasi",
"StrategiPerawatan",
"JenisSensor",
"StandarAmbangBatas",
"TipsDiagnostik",
"RingkasanKeputusan",
"RincianManfaatTahunan",
"AnalisisEkonomi",
"AnalisisKejadianUtama",
"AkibatJikaTidakDiatasi",
"EstimasiBiayaPenanggulangan",
"InformasiLaporan",
"Lainnya",
"FileAttachment" # Added intent for file attachment
]
)
equipment_name: str = Field(
description="Nama equipment yang dibahas atau dimasukkan oleh pengguna. Jika tidak ada nama equipment yang relevan, berikan string kosong dengan respon ''.",
enum=equipment_keywords
)
function_call: str = Field(
description="Kebutuhan untuk melakukan function call jika masukkan pengguna terkait data reliability atau sensor equipment. Data reliability equipment terdiri dari nilai reliability, MTTR, MTBF, MDT, failure rate, next failure, dan jumlah failure historis equipment. Sedangkan data sensor terdiri dari nama sensor, status sensor, dan value sensor per equipment. Identifikasi hasil berdasarkan enum",
enum=["Reliability", "MTTR", "MTBF", "Sensor", "MDT"]
)
confidence: float = Field(description="Level kepercayaan hasil dari identifikasi (0-1).")
location_tag: float = Field(description="Unique indentifier equipment.")
class ClassifierResponse(BaseModel):
intent_detection: IntentDetection
response: str
class UpdateFeedback(BaseModel):
update: str # "good" | "bad"
comment: Optional[str] = None
screenshot: Optional[str] = None # base64 compressed image
class EvaluateRequest(DefaultBase):
response_id: UUID
user_id: UUID
user_message: str
ai_response: str
context: Optional[List[str]] = None
ground_truth: str
class EvaluateResponse(DefaultBase):
faithfullness: float
answer_relevance: float
context_recall: float
context_relevancy: float
class RootCauseRequest(BaseModel):
equipment_type: Optional[str] = Field(None, description="Tipe dari equipment, contoh: centrifugal_pump")
problem: Optional[str] = Field(None, description="Masalah spesifik yang dialami, contoh: high_vibration")
class RootCauseResponse(BaseModel):
equipment_type: str
problem_observed: Optional[str] = None
possible_causes_to_check: Optional[List[str]] = None
available_problems: Optional[List[str]] = None
available_equipment_types: Optional[List[str]] = None
instruction_for_llm: str
error: Optional[str] = None
class ModelRead(BaseModel):
name: str
display_name: str
description: Optional[str] = None
provider: str
class AdminModelRead(BaseModel):
name: str
display_name: str
model: str
model_server: str
api_key: str
provider: str
description: Optional[str] = None
stream: bool
enabled: bool
is_default: bool
generate_cfg: Optional[dict] = None
class ModelConfigCreate(BaseModel):
name: str
display_name: str
model: str
model_server: str
api_key: Optional[str] = "not-needed"
provider: Optional[str] = "openai"
description: Optional[str] = None
stream: Optional[bool] = True
enabled: Optional[bool] = True
generate_cfg: Optional[dict] = None
class ModelConfigUpdate(BaseModel):
display_name: str
model: str
model_server: str
api_key: Optional[str] = "not-needed"
provider: Optional[str] = "openai"
description: Optional[str] = None
stream: Optional[bool] = True
enabled: Optional[bool] = True
generate_cfg: Optional[dict] = None
class MemoryRead(DefaultBase):
"""A private, user-owned long-term memory shown in the Memory Lab."""
id: UUID
conversation_id: Optional[UUID] = None
source_type: str
title: Optional[str] = None
summary: str
excerpt: Optional[str] = None
location_tag: Optional[str] = None
equipment: Optional[str] = None
confidence: Optional[float] = None
created_at: Optional[datetime] = None
class MemoryLabSnapshot(DefaultBase):
"""Safe developer-facing view of the current user's private memories."""
items: List[MemoryRead] = Field(default_factory=list)
total: int = 0
retrieval_limit: int = 50
explicit_only: bool = True
user_private: bool = True
secrets_blocked: bool = True