Commit 0d12d87f authored by Administrator's avatar Administrator

Update 7 files via Son of Anton

parent 015b7902
...@@ -42,13 +42,14 @@ def _run_migrations(): ...@@ -42,13 +42,14 @@ def _run_migrations():
conn.execute(text("ALTER TABLE chats ADD COLUMN reasoning_budget INTEGER DEFAULT 0")) conn.execute(text("ALTER TABLE chats ADD COLUMN reasoning_budget INTEGER DEFAULT 0"))
if "linked_repo_id" not in columns: if "linked_repo_id" not in columns:
conn.execute(text("ALTER TABLE chats ADD COLUMN linked_repo_id VARCHAR(36)")) conn.execute(text("ALTER TABLE chats ADD COLUMN linked_repo_id VARCHAR(36)"))
if "linked_repo_branch" not in columns:
conn.execute(text("ALTER TABLE chats ADD COLUMN linked_repo_branch VARCHAR(100)"))
conn.commit() conn.commit()
if "chat_attachments" not in existing_tables: if "chat_attachments" not in existing_tables:
from backend.models import ChatAttachment from backend.models import ChatAttachment
ChatAttachment.__table__.create(bind=engine, checkfirst=True) ChatAttachment.__table__.create(bind=engine, checkfirst=True)
# Create user_permissions table if missing
if "user_permissions" not in existing_tables: if "user_permissions" not in existing_tables:
from backend.models import UserPermissions from backend.models import UserPermissions
UserPermissions.__table__.create(bind=engine, checkfirst=True) UserPermissions.__table__.create(bind=engine, checkfirst=True)
......
...@@ -54,7 +54,6 @@ class UserPermissions(Base): ...@@ -54,7 +54,6 @@ class UserPermissions(Base):
unique=True, nullable=False, index=True, unique=True, nullable=False, index=True,
) )
# Feature access
can_use_web_search = Column(Boolean, default=False) can_use_web_search = Column(Boolean, default=False)
can_use_ui_design = Column(Boolean, default=False) can_use_ui_design = Column(Boolean, default=False)
can_use_knowledge_base = Column(Boolean, default=True) can_use_knowledge_base = Column(Boolean, default=True)
...@@ -63,10 +62,8 @@ class UserPermissions(Base): ...@@ -63,10 +62,8 @@ class UserPermissions(Base):
can_export_pptx = Column(Boolean, default=True) can_export_pptx = Column(Boolean, default=True)
can_export_docx = Column(Boolean, default=True) can_export_docx = Column(Boolean, default=True)
# Model access — "all" or comma-separated model IDs
allowed_models = Column(Text, default="eu.anthropic.claude-haiku-4-5-20251001-v1:0") allowed_models = Column(Text, default="eu.anthropic.claude-haiku-4-5-20251001-v1:0")
# Limits (0 = unlimited for count-based limits)
max_tokens_cap = Column(Integer, default=4096) max_tokens_cap = Column(Integer, default=4096)
max_reasoning_budget = Column(Integer, default=0) max_reasoning_budget = Column(Integer, default=0)
max_chats = Column(Integer, default=50) max_chats = Column(Integer, default=50)
...@@ -90,6 +87,7 @@ class Chat(Base): ...@@ -90,6 +87,7 @@ class Chat(Base):
model = Column(String(100), default="eu.anthropic.claude-opus-4-6-v1") model = Column(String(100), default="eu.anthropic.claude-opus-4-6-v1")
knowledge_base_id = Column(String(36), nullable=True) knowledge_base_id = Column(String(36), nullable=True)
linked_repo_id = Column(String(36), nullable=True) linked_repo_id = Column(String(36), nullable=True)
linked_repo_branch = Column(String(100), nullable=True)
max_tokens = Column(Integer, default=4096) max_tokens = Column(Integer, default=4096)
reasoning_budget = Column(Integer, default=0) reasoning_budget = Column(Integer, default=0)
created_at = Column(DateTime, default=datetime.utcnow) created_at = Column(DateTime, default=datetime.utcnow)
......
...@@ -28,6 +28,7 @@ class CreateChatBody(BaseModel): ...@@ -28,6 +28,7 @@ class CreateChatBody(BaseModel):
model: str = "eu.anthropic.claude-opus-4-6-v1" model: str = "eu.anthropic.claude-opus-4-6-v1"
knowledge_base_id: Optional[str] = None knowledge_base_id: Optional[str] = None
linked_repo_id: Optional[str] = None linked_repo_id: Optional[str] = None
linked_repo_branch: Optional[str] = None
max_tokens: int = 4096 max_tokens: int = 4096
reasoning_budget: int = 0 reasoning_budget: int = 0
...@@ -39,6 +40,7 @@ class UpdateChatBody(BaseModel): ...@@ -39,6 +40,7 @@ class UpdateChatBody(BaseModel):
reasoning_budget: Optional[int] = None reasoning_budget: Optional[int] = None
knowledge_base_id: Optional[str] = None knowledge_base_id: Optional[str] = None
linked_repo_id: Optional[str] = None linked_repo_id: Optional[str] = None
linked_repo_branch: Optional[str] = None
class SendMessageBody(BaseModel): class SendMessageBody(BaseModel):
...@@ -85,6 +87,7 @@ def create_chat(body: CreateChatBody, user: User = Depends(get_current_user), db ...@@ -85,6 +87,7 @@ def create_chat(body: CreateChatBody, user: User = Depends(get_current_user), db
model=check_model_allowed(user.id, body.model, db), model=check_model_allowed(user.id, body.model, db),
knowledge_base_id=body.knowledge_base_id or None, knowledge_base_id=body.knowledge_base_id or None,
linked_repo_id=body.linked_repo_id or None, linked_repo_id=body.linked_repo_id or None,
linked_repo_branch=body.linked_repo_branch or None,
max_tokens=min(body.max_tokens, perms.get("max_tokens_cap", 4096)), max_tokens=min(body.max_tokens, perms.get("max_tokens_cap", 4096)),
reasoning_budget=min(body.reasoning_budget, perms.get("max_reasoning_budget", 0)), reasoning_budget=min(body.reasoning_budget, perms.get("max_reasoning_budget", 0)),
) )
...@@ -116,6 +119,8 @@ def update_chat(chat_id: str, body: UpdateChatBody, user: User = Depends(get_cur ...@@ -116,6 +119,8 @@ def update_chat(chat_id: str, body: UpdateChatBody, user: User = Depends(get_cur
if body.linked_repo_id and not perms.get("can_use_gitlab"): if body.linked_repo_id and not perms.get("can_use_gitlab"):
raise HTTPException(403, "GitLab not enabled.") raise HTTPException(403, "GitLab not enabled.")
chat.linked_repo_id = body.linked_repo_id or None chat.linked_repo_id = body.linked_repo_id or None
if body.linked_repo_branch is not None:
chat.linked_repo_branch = body.linked_repo_branch or None
db.commit() db.commit()
return _chat_dict(chat, db) return _chat_dict(chat, db)
...@@ -205,11 +210,6 @@ async def commit_from_chat( ...@@ -205,11 +210,6 @@ async def commit_from_chat(
user: User = Depends(get_current_user), user: User = Depends(get_current_user),
db: Session = Depends(get_db), db: Session = Depends(get_db),
): ):
"""
Commit files from chat to linked GitLab repo.
Auto-detects whether each file should be 'create' or 'update'
by checking the repo tree, so it never fails on wrong action type.
"""
check_feature(user.id, "use_gitlab", db) check_feature(user.id, "use_gitlab", db)
chat = db.query(Chat).filter(Chat.id == chat_id, Chat.user_id == user.id).first() chat = db.query(Chat).filter(Chat.id == chat_id, Chat.user_id == user.id).first()
...@@ -226,73 +226,44 @@ async def commit_from_chat( ...@@ -226,73 +226,44 @@ async def commit_from_chat(
if not settings or not settings.is_active: if not settings or not settings.is_active:
raise HTTPException(400, "GitLab not configured") raise HTTPException(400, "GitLab not configured")
# ── Fetch repo tree to know which files already exist ──
existing_paths = set() existing_paths = set()
try: try:
tree = await gitlab_service.get_tree( tree = await gitlab_service.get_tree(
settings.gitlab_url, settings.gitlab_url, settings.gitlab_token,
settings.gitlab_token, repo.gitlab_project_id, ref=body.branch, recursive=True,
repo.gitlab_project_id,
ref=body.branch,
recursive=True,
) )
existing_paths = { existing_paths = {item["path"] for item in tree if item["type"] == "blob"}
item["path"] for item in tree if item["type"] == "blob"
}
except Exception: except Exception:
# If tree fetch fails (empty repo, network issue, etc.),
# we'll try all as "create" since we can't know what exists
pass pass
# ── Build actions with auto-detected create/update ──
actions = [] actions = []
for f in body.files: for f in body.files:
file_path = f.get("file_path", "") file_path = f.get("file_path", "")
content = f.get("content", "") content = f.get("content", "")
requested_action = f.get("action", "auto") requested_action = f.get("action", "auto")
if not file_path or not content: if not file_path or not content:
continue continue
file_exists = file_path in existing_paths file_exists = file_path in existing_paths
# Smart action resolution
if requested_action in ("auto", "upsert"): if requested_action in ("auto", "upsert"):
# Auto-detect: update if exists, create if not
actual_action = "update" if file_exists else "create" actual_action = "update" if file_exists else "create"
elif requested_action == "update" and not file_exists: elif requested_action == "update" and not file_exists:
# User said update but file doesn't exist → create instead
actual_action = "create" actual_action = "create"
elif requested_action == "create" and file_exists: elif requested_action == "create" and file_exists:
# User said create but file already exists → update instead
actual_action = "update" actual_action = "update"
else: else:
actual_action = requested_action actual_action = requested_action
actions.append({"action": actual_action, "file_path": file_path, "content": content})
actions.append({
"action": actual_action,
"file_path": file_path,
"content": content,
})
if not actions: if not actions:
raise HTTPException(400, "No valid files to commit") raise HTTPException(400, "No valid files to commit")
try: try:
result = await gitlab_service.commit_files( result = await gitlab_service.commit_files(
settings.gitlab_url, settings.gitlab_url, settings.gitlab_token,
settings.gitlab_token, repo.gitlab_project_id, body.branch, body.commit_message, actions,
repo.gitlab_project_id,
body.branch,
body.commit_message,
actions,
) )
gen_manager.invalidate_repo_cache(repo.id) gen_manager.invalidate_repo_cache(repo.id)
return { return {"ok": True, "commit": result, "files_committed": len(actions)}
"ok": True,
"commit": result,
"files_committed": len(actions),
}
except gitlab_service.GitLabError as e: except gitlab_service.GitLabError as e:
raise HTTPException(e.status_code, f"Commit failed: {e.detail}") raise HTTPException(e.status_code, f"Commit failed: {e.detail}")
...@@ -314,6 +285,7 @@ def _chat_dict(c, db=None): ...@@ -314,6 +285,7 @@ def _chat_dict(c, db=None):
"id": c.id, "title": c.title, "model": c.model, "id": c.id, "title": c.title, "model": c.model,
"knowledge_base_id": c.knowledge_base_id, "knowledge_base_id": c.knowledge_base_id,
"linked_repo_id": c.linked_repo_id, "linked_repo_id": c.linked_repo_id,
"linked_repo_branch": getattr(c, "linked_repo_branch", None) or None,
"max_tokens": c.max_tokens or 4096, "max_tokens": c.max_tokens or 4096,
"reasoning_budget": c.reasoning_budget or 0, "reasoning_budget": c.reasoning_budget or 0,
"created_at": str(c.created_at), "created_at": str(c.created_at),
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment