Skip to main content
Glama
models.py7.67 kB
"""Functional data model helpers for Jules Job Manager.""" from __future__ import annotations from copy import deepcopy from datetime import datetime from typing import Dict, Iterable, List, Optional TASK_STATUS_VALUES: List[str] = [ "pending", "in_progress", "completed", "paused", "waiting_approval", ] CHAT_ROLES: List[str] = ["user", "jules", "system"] def get_task_status_values() -> List[str]: """Return all allowed task status values.""" return list(TASK_STATUS_VALUES) def validate_task_status(status: str) -> None: """Ensure the provided status string is valid.""" if status not in TASK_STATUS_VALUES: message = f"Unsupported task status: {status}" raise ValueError(message) def _ensure_timestamp(timestamp: Optional[str]) -> str: if timestamp is None: now = datetime.now().astimezone() return now.isoformat() datetime.fromisoformat(timestamp) return timestamp def create_chat_message(role: str, content: str, timestamp: Optional[str] = None) -> Dict[str, str]: """Create a chat message dictionary with validated data.""" if role not in CHAT_ROLES: message = f"Unsupported chat role: {role}" raise ValueError(message) if not content: raise ValueError("Chat message content cannot be empty") resolved_timestamp = _ensure_timestamp(timestamp) message_dict: Dict[str, str] = { "type": role, "content": content, "timestamp": resolved_timestamp, } return message_dict def chat_message_to_dict(message: Dict[str, str]) -> Dict[str, str]: """Return a shallow copy of a chat message dictionary for serialization.""" return dict(message) def chat_message_from_dict(data: Dict[str, str]) -> Dict[str, str]: """Validate and return a chat message dictionary from raw data.""" role = data.get("type") content = data.get("content") timestamp = data.get("timestamp") return create_chat_message(role, content, timestamp) def create_source_file( filename: str, url: str, status: str, diff: Optional[str] = None, ) -> Dict[str, Optional[str]]: """Create a source file dictionary containing metadata.""" if not filename: raise ValueError("Source file filename cannot be empty") if not url: raise ValueError("Source file URL cannot be empty") if not status: raise ValueError("Source file status cannot be empty") file_dict: Dict[str, Optional[str]] = { "filename": filename, "url": url, "status": status, "diff": diff, } return file_dict def source_file_to_dict(file_dict: Dict[str, Optional[str]]) -> Dict[str, Optional[str]]: """Return a shallow copy of a source file dictionary for serialization.""" return dict(file_dict) def source_file_from_dict(data: Dict[str, Optional[str]]) -> Dict[str, Optional[str]]: """Validate and return a source file dictionary from raw data.""" filename = data.get("filename") url = data.get("url") status = data.get("status") diff = data.get("diff") return create_source_file(filename, url, status, diff) def _ensure_datetime(value: datetime) -> datetime: if not isinstance(value, datetime): message = "Expected datetime instance" raise TypeError(message) if value.tzinfo is None: return value.replace(tzinfo=datetime.now().astimezone().tzinfo) return value def _normalize_chat_history(history: Optional[Iterable[Dict[str, str]]]) -> List[Dict[str, str]]: normalized: List[Dict[str, str]] = [] if history is None: return normalized for item in history: normalized.append(chat_message_from_dict(item)) return normalized def _normalize_source_files(files: Optional[Iterable[Dict[str, Optional[str]]]]) -> List[Dict[str, Optional[str]]]: normalized: List[Dict[str, Optional[str]]] = [] if files is None: return normalized for item in files: normalized.append(source_file_from_dict(item)) return normalized def create_jules_task( task_id: str, title: str, description: str, repository: str, branch: str, status: str, created_at: datetime, updated_at: datetime, url: str, chat_history: Optional[Iterable[Dict[str, str]]] = None, source_files: Optional[Iterable[Dict[str, Optional[str]]]] = None, ) -> Dict[str, object]: """Create a Jules task dictionary with validated contents.""" if not task_id: raise ValueError("Task identifier cannot be empty") if not title: raise ValueError("Task title cannot be empty") if not repository: raise ValueError("Repository cannot be empty") if not branch: raise ValueError("Branch cannot be empty") if not url: raise ValueError("Task URL cannot be empty") validate_task_status(status) normalized_created = _ensure_datetime(created_at) normalized_updated = _ensure_datetime(updated_at) normalized_history = _normalize_chat_history(chat_history) normalized_files = _normalize_source_files(source_files) task_dict: Dict[str, object] = { "id": task_id, "title": title, "description": description, "repository": repository, "branch": branch, "status": status, "created_at": normalized_created, "updated_at": normalized_updated, "url": url, "chat_history": normalized_history, "source_files": normalized_files, } return task_dict def jules_task_to_dict(task: Dict[str, object]) -> Dict[str, object]: """Convert an in-memory task dictionary to a JSON-serializable dict.""" serialized: Dict[str, object] = {} for key, value in task.items(): serialized[key] = value created_at = serialized.get("created_at") updated_at = serialized.get("updated_at") if isinstance(created_at, datetime): serialized["created_at"] = created_at.isoformat() if isinstance(updated_at, datetime): serialized["updated_at"] = updated_at.isoformat() history = serialized.get("chat_history", []) new_history: List[Dict[str, str]] = [] for item in history: new_history.append(chat_message_to_dict(item)) serialized["chat_history"] = new_history files = serialized.get("source_files", []) new_files: List[Dict[str, Optional[str]]] = [] for item in files: new_files.append(source_file_to_dict(item)) serialized["source_files"] = new_files return serialized def jules_task_from_dict(data: Dict[str, object]) -> Dict[str, object]: """Create a task dictionary from serialized data.""" status = data.get("status") created_at_value = data.get("created_at") updated_at_value = data.get("updated_at") if not isinstance(created_at_value, datetime): created_at_value = datetime.fromisoformat(str(created_at_value)) if not isinstance(updated_at_value, datetime): updated_at_value = datetime.fromisoformat(str(updated_at_value)) chat_history_input = data.get("chat_history") source_files_input = data.get("source_files") return create_jules_task( str(data.get("id")), str(data.get("title")), str(data.get("description")), str(data.get("repository")), str(data.get("branch")), str(status), created_at_value, updated_at_value, str(data.get("url")), chat_history_input, source_files_input, ) def clone_jules_task(task: Dict[str, object]) -> Dict[str, object]: """Return a deep copy of a task dictionary.""" return deepcopy(task)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mberjans/google-jules-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server