1 line
65 KiB
JSON
1 line
65 KiB
JSON
{"repo": "chemark/ai-agent-book", "files": {"chapter3/contextual-retrieval-for-user-memory/config.py": {"5687624e1b5c85969472c07d915f8da69189a9ab": {"t": 1785746634.84985, "n": 10939, "h": "fada819270afed92", "c": "\"\"\"Configuration for Agentic RAG User Memory Evaluation System\"\"\"\n\nimport os\nfrom dataclasses import dataclass, field\nfrom typing import Optional, Dict, Any, List\nfrom enum import Enum\nfrom pathlib import Path\n\n\ndef _reasoning_safe_temperature(model, requested=1.0):\n \"\"\"Reasoning models (Kimi K3, GPT-5, ...) only accept temperature=1.\n Return 1 for those; otherwise the requested value so non-reasoning\n providers (Doubao, DeepSeek, older Moonshot) are unchanged.\"\"\"\n m = str(model or \"\").lower().replace(\"/\", \"-\")\n return 1 if (\"kimi-k3\" in m or \"gpt-5\" in m) else requested\n\n\ndef _openrouter_model_id(model: Optional[str]) -> str:\n \"\"\"Map a provider-native model name to an OpenRouter model id, used by the\n universal OpenRouter fallback. An explicit OPENROUTER_MODEL env var wins.\"\"\"\n override = os.getenv(\"OPENROUTER_MODEL\")\n if override:\n return override\n m = (model or \"\").strip()\n if not m:\n return \"openai/gpt-5.6-luna\"\n if \"/\" in m:\n return m # already an OpenRouter-style id (e.g. openai/gpt-5.6-luna)\n ml = m.lower()\n if ml.startswith((\"gpt-\", \"o1\", \"o3\", \"o4\", \"chatgpt\")):\n return \"openai/\" + m\n if ml.startswith(\"claude-\"):\n return \"anthropic/claude-opus-4.8\"\n if ml.startswith(\"kimi\"):\n # kimi-k3 is not on OpenRouter; moonshotai/kimi-k2.6 is the closest hosted id.\n return \"moonshotai/kimi-k2.6\"\n # Provider-native ids (kimi-*/doubao-*/qwen/deepseek-*) not hosted on\n # OpenRouter under the same name -> a widely-available OpenAI chat model.\n return \"openai/gpt-5.6-luna\"\n\n\nclass Provider(str, Enum):\n \"\"\"Supported LLM providers\"\"\"\n SILICONFLOW = \"siliconflow\"\n DOUBAO = \"doubao\"\n KIMI = \"kimi\"\n MOONSHOT = \"moonshot\"\n OPENROUTER = \"openrouter\"\n OPENAI = \"openai\"\n GROQ = \"groq\"\n TOGETHER = \"together\"\n DEEPSEEK = \"deepseek\"\n\n\nclass IndexMode(str, Enum):\n \"\"\"Indexing modes for conversation chunks\"\"\"\n DENSE = \"dense\" # Dense embedding only\n SPARSE = \"sparse\" # Sparse embedding only (BM25)\n HYBRID = \"hybrid\" # Both dense and sparse\n\n\nclass ChunkingStrategy(str, Enum):\n \"\"\"Strategies for chunking conversations\"\"\"\n FIXED_ROUNDS = \"fixed_rounds\" # Fixed number of rounds per chunk\n SEMANTIC = \"semantic\" # Semantic boundaries\n TIME_BASED = \"time_based\" # Based on timestamp gaps\n\n\n@dataclass\nclass LLMConfig:\n \"\"\"LLM configuration\"\"\"\n provider: str = \"kimi\" # Default provider\n model: Optional[str] = None # Will use provider defaults if not specified\n api_key: Optional[str] = None # Will read from env if not provided\n temperature: float = 0.7\n max_tokens: int = 2048\n stream: bool = True\n \n # Provider-specific defaults\n PROVIDER_DEFAULTS = {\n \"siliconflow\": {\n \"model\": \"Qwen/Qwen3-235B-A22B-Thinking-2507\",\n \"base_url\": \"https://api.siliconflow.cn/v1\"\n },\n \"doubao\": {\n \"model\": \"doubao-seed-1-6-thinking-250715\",\n \"base_url\": \"https://ark.cn-beijing.volces.com/api/v3\"\n },\n \"kimi\": {\n \"model\": \"kimi-k3\",\n \"base_url\": \"https://api.moonshot.cn/v1\"\n },\n \"moonshot\": {\n \"model\": \"kimi-k3\",\n \"base_url\": \"https://api.moonshot.cn/v1\"\n },\n \"openrouter\": {\n \"model\": \"openai/gpt-5.6-luna\",\n \"base_url\": \"https://openrouter.ai/api/v1\"\n },\n \"openai\": {\n \"model\": \"gpt-5.6-luna\",\n \"base_url\": \"https://api.openai.com/v1\"\n },\n \"groq\": {\n \"model\": \"llama-3.3-70b-versatile\",\n \"base_url\": \"https://api.groq.com/openai/v1\"\n },\n \"together\": {\n \"model\": \"meta-llama/Llama-3.3-70B-Instruct-Turbo\",\n \"base_url\": \"https://api.together.xyz\"\n },\n \"deepseek\": {\n \"model\": \"deepseek-reasoner\",\n \"base_url\": \"https://api.deepseek.com/v1\"\n }\n }\n \n def get_client_config(self) -> tuple[Dict[str, Any], str]:\n \"\"\"Get OpenAI client configuration\"\"\"\n provider = self.provider.lower()\n defaults = self.PROVIDER_DEFAULTS.get(provider, {})\n \n # Determine API key\n api_key = self.api_key or os.getenv(f\"{provider.upper()}_API_KEY\")\n if not api_key and provider == \"moonshot\":\n api_key = os.getenv(\"KIMI_API_KEY\") # Fallback for moonshot\n\n # Determine model\n model = self.model or defaults.get(\"model\", \"gpt-5.6-luna\")\n\n # Universal OpenRouter fallback: primary provider key absent but\n # OPENROUTER_API_KEY present -> route through OpenRouter.\n if not api_key and provider != \"openrouter\" and os.getenv(\"OPENROUTER_API_KEY\"):\n return {\n \"api_key\": os.getenv(\"OPENROUTER_API_KEY\"),\n \"base_url\": \"https://openrouter.ai/api/v1\",\n }, _openrouter_model_id(model)\n\n # Build client config\n client_config = {\"api_key\": api_key}\n \n # Add base URL if needed\n if base_url := defaults.get(\"base_url\"):\n client_config[\"base_url\"] = base_url\n \n return client_config, model\n\n\n@dataclass\nclass ChunkingConfig:\n \"\"\"Configuration for conversation chunking\"\"\"\n strategy: ChunkingStrategy = ChunkingStrategy.FIXED_ROUNDS\n rounds_per_chunk: int = 20 # Number of rounds per chunk for FIXED_ROUNDS\n overlap_rounds: int = 2 # Number of overlapping rounds between chunks\n include_metadata: bool = True # Include conversation metadata in chunks\n min_chunk_size: int = 5 # Minimum number of rounds in a chunk\n max_chunk_size: int = 50 # Maximum number of rounds in a chunk\n\n\n@dataclass\nclass IndexConfig:\n \"\"\"Configuration for RAG indexing\"\"\"\n mode: IndexMode = IndexMode.HYBRID\n embedding_model: str = \"text-embedding-3-small\" # OpenAI embedding model\n embedding_dim: int = 1536 # Dimension of embeddings\n index_path: str = \"indexes/memory_index\"\n chunk_store_path: str = \"data/chunk_store.json\"\n enable_contextual: bool = True # Add contextual information to chunks\n contextual_window: int = 2 # Number of surrounding rounds for context\n\n\n@dataclass\nclass EvaluationConfig:\n \"\"\"Configuration for evaluation framework\"\"\"\n test_cases_dir: str = \"../../week2/user-memory-evaluation/test_cases\"\n results_dir: str = \"results\"\n enable_verbose: bool = True\n save_trajectories: bool = True\n max_iterations: int = 10 # Max iterations for ReAct pattern\n enable_caching: bool = True # Cache indexed conversations\n use_llm_judge: bool = False # Use LLM to evaluate answers\n\n\n@dataclass\nclass AgentConfig:\n \"\"\"Agent behavior configuration\"\"\"\n enable_reasoning: bool = True # Show reasoning steps\n enable_citations: bool = True # Include citations in responses\n max_search_results: int = 5 # Maximum search results to consider\n confidence_threshold: float = 0.7 # Minimum confidence for answers\n enable_multi_search: bool = True # Allow multiple searches per query\n max_searches_per_query: int = 3 # Maximum searches allowed\n verbose: bool = True # Enable verbose output\n\n\n@dataclass\nclass Config:\n \"\"\"Main configuration container\"\"\"\n llm: LLMConfig = field(default_factory=LLMConfig)\n chunking: ChunkingConfig = field(default_factory=ChunkingConfig)\n index: IndexConfig = field(default_factory=IndexConfig)\n evaluation: EvaluationConfig = field(default_factory=EvaluationConfig)\n agent: AgentConfig = field(default_factory=AgentConfig)\n \n @classmethod\n def from_env(cls) -> \"Config\":\n \"\"\"Create configuration from environment variables\"\"\"\n config = cls()\n \n # Override with environment variables\n if provider := os.getenv(\"LLM_PROVIDER\"):\n config.llm.provider = provider\n \n if model := os.getenv(\"LLM_MODEL\"):\n config.llm.model = model\n \n if rounds := os.getenv(\"ROUNDS_PER_CHUNK\"):\n config.chunking.rounds_per_chunk = int(rounds)\n \n if index_mode := os.getenv(\"INDEX_MODE\"):\n config.index.mode = IndexMode(index_mode)\n \n return config\n \n def save(self, path: str):\n \"\"\"Save configuration to JSON file\"\"\"\n import json\n \n config_dict = {\n \"llm\": {\n \"provider\": self.llm.provider,\n \"model\": self.llm.model,\n \"temperature\": _reasoning_safe_temperature(self.llm.model, self.llm.temperature),\n \"max_tokens\": self.llm.max_tokens,\n \"stream\": self.llm.stream\n },\n \"chunking\": {\n \"strategy\": self.chunking.strategy,\n \"rounds_per_chunk\": self.chunking.rounds_per_chunk,\n \"overlap_rounds\": self.chunking.overlap_rounds,\n \"include_metadata\": self.chunking.include_metadata\n },\n \"index\": {\n \"mode\": self.index.mode,\n \"embedding_model\": self.index.embedding_model,\n \"enable_contextual\": self.index.enable_contextual,\n \"contextual_window\": self.index.contextual_window\n },\n \"evaluation\": {\n \"enable_verbose\": self.evaluation.enable_verbose,\n \"save_trajectories\": self.evaluation.save_trajectories,\n \"max_iterations\": self.evaluation.max_iterations\n },\n \"agent\": {\n \"enable_reasoning\": self.agent.enable_reasoning,\n \"enable_citations\": self.agent.enable_citations,\n \"max_search_results\": self.agent.max_search_results,\n \"confidence_threshold\": self.agent.confidence_threshold\n }\n }\n \n with open(path, 'w') as f:\n json.dump(config_dict, f, indent=2)\n \n @classmethod\n def load(cls, path: str) -> \"Config\":\n \"\"\"Load configuration from JSON file\"\"\"\n import json\n \n with open(path, 'r') as f:\n config_dict = json.load(f)\n \n config = cls()\n \n # Update LLM config\n if \"llm\" in config_dict:\n for key, value in config_dict[\"llm\"].items():\n setattr(config.llm, key, value)\n \n # Update other configs similarly\n for section in [\"chunking\", \"index\", \"evaluation\", \"agent\"]:\n if section in config_dict:\n section_config = getattr(config, section)\n for key, value in config_dict[section].items():\n # Handle enums\n if key == \"strategy\" and section == \"chunking\":\n value = ChunkingStrategy(value)\n elif key == \"mode\" and section == \"index\":\n value = IndexMode(value)\n setattr(section_config, key, value)\n \n return config\n"}, "a41da78733b36e18693d409fd835542b0ee4ae5a": {"t": 1785766441.618802, "n": 10939, "h": "fada819270afed92", "c": "\"\"\"Configuration for Agentic RAG User Memory Evaluation System\"\"\"\n\nimport os\nfrom dataclasses import dataclass, field\nfrom typing import Optional, Dict, Any, List\nfrom enum import Enum\nfrom pathlib import Path\n\n\ndef _reasoning_safe_temperature(model, requested=1.0):\n \"\"\"Reasoning models (Kimi K3, GPT-5, ...) only accept temperature=1.\n Return 1 for those; otherwise the requested value so non-reasoning\n providers (Doubao, DeepSeek, older Moonshot) are unchanged.\"\"\"\n m = str(model or \"\").lower().replace(\"/\", \"-\")\n return 1 if (\"kimi-k3\" in m or \"gpt-5\" in m) else requested\n\n\ndef _openrouter_model_id(model: Optional[str]) -> str:\n \"\"\"Map a provider-native model name to an OpenRouter model id, used by the\n universal OpenRouter fallback. An explicit OPENROUTER_MODEL env var wins.\"\"\"\n override = os.getenv(\"OPENROUTER_MODEL\")\n if override:\n return override\n m = (model or \"\").strip()\n if not m:\n return \"openai/gpt-5.6-luna\"\n if \"/\" in m:\n return m # already an OpenRouter-style id (e.g. openai/gpt-5.6-luna)\n ml = m.lower()\n if ml.startswith((\"gpt-\", \"o1\", \"o3\", \"o4\", \"chatgpt\")):\n return \"openai/\" + m\n if ml.startswith(\"claude-\"):\n return \"anthropic/claude-opus-4.8\"\n if ml.startswith(\"kimi\"):\n # kimi-k3 is not on OpenRouter; moonshotai/kimi-k2.6 is the closest hosted id.\n return \"moonshotai/kimi-k2.6\"\n # Provider-native ids (kimi-*/doubao-*/qwen/deepseek-*) not hosted on\n # OpenRouter under the same name -> a widely-available OpenAI chat model.\n return \"openai/gpt-5.6-luna\"\n\n\nclass Provider(str, Enum):\n \"\"\"Supported LLM providers\"\"\"\n SILICONFLOW = \"siliconflow\"\n DOUBAO = \"doubao\"\n KIMI = \"kimi\"\n MOONSHOT = \"moonshot\"\n OPENROUTER = \"openrouter\"\n OPENAI = \"openai\"\n GROQ = \"groq\"\n TOGETHER = \"together\"\n DEEPSEEK = \"deepseek\"\n\n\nclass IndexMode(str, Enum):\n \"\"\"Indexing modes for conversation chunks\"\"\"\n DENSE = \"dense\" # Dense embedding only\n SPARSE = \"sparse\" # Sparse embedding only (BM25)\n HYBRID = \"hybrid\" # Both dense and sparse\n\n\nclass ChunkingStrategy(str, Enum):\n \"\"\"Strategies for chunking conversations\"\"\"\n FIXED_ROUNDS = \"fixed_rounds\" # Fixed number of rounds per chunk\n SEMANTIC = \"semantic\" # Semantic boundaries\n TIME_BASED = \"time_based\" # Based on timestamp gaps\n\n\n@dataclass\nclass LLMConfig:\n \"\"\"LLM configuration\"\"\"\n provider: str = \"kimi\" # Default provider\n model: Optional[str] = None # Will use provider defaults if not specified\n api_key: Optional[str] = None # Will read from env if not provided\n temperature: float = 0.7\n max_tokens: int = 2048\n stream: bool = True\n \n # Provider-specific defaults\n PROVIDER_DEFAULTS = {\n \"siliconflow\": {\n \"model\": \"Qwen/Qwen3-235B-A22B-Thinking-2507\",\n \"base_url\": \"https://api.siliconflow.cn/v1\"\n },\n \"doubao\": {\n \"model\": \"doubao-seed-1-6-thinking-250715\",\n \"base_url\": \"https://ark.cn-beijing.volces.com/api/v3\"\n },\n \"kimi\": {\n \"model\": \"kimi-k3\",\n \"base_url\": \"https://api.moonshot.cn/v1\"\n },\n \"moonshot\": {\n \"model\": \"kimi-k3\",\n \"base_url\": \"https://api.moonshot.cn/v1\"\n },\n \"openrouter\": {\n \"model\": \"openai/gpt-5.6-luna\",\n \"base_url\": \"https://openrouter.ai/api/v1\"\n },\n \"openai\": {\n \"model\": \"gpt-5.6-luna\",\n \"base_url\": \"https://api.openai.com/v1\"\n },\n \"groq\": {\n \"model\": \"llama-3.3-70b-versatile\",\n \"base_url\": \"https://api.groq.com/openai/v1\"\n },\n \"together\": {\n \"model\": \"meta-llama/Llama-3.3-70B-Instruct-Turbo\",\n \"base_url\": \"https://api.together.xyz\"\n },\n \"deepseek\": {\n \"model\": \"deepseek-reasoner\",\n \"base_url\": \"https://api.deepseek.com/v1\"\n }\n }\n \n def get_client_config(self) -> tuple[Dict[str, Any], str]:\n \"\"\"Get OpenAI client configuration\"\"\"\n provider = self.provider.lower()\n defaults = self.PROVIDER_DEFAULTS.get(provider, {})\n \n # Determine API key\n api_key = self.api_key or os.getenv(f\"{provider.upper()}_API_KEY\")\n if not api_key and provider == \"moonshot\":\n api_key = os.getenv(\"KIMI_API_KEY\") # Fallback for moonshot\n\n # Determine model\n model = self.model or defaults.get(\"model\", \"gpt-5.6-luna\")\n\n # Universal OpenRouter fallback: primary provider key absent but\n # OPENROUTER_API_KEY present -> route through OpenRouter.\n if not api_key and provider != \"openrouter\" and os.getenv(\"OPENROUTER_API_KEY\"):\n return {\n \"api_key\": os.getenv(\"OPENROUTER_API_KEY\"),\n \"base_url\": \"https://openrouter.ai/api/v1\",\n }, _openrouter_model_id(model)\n\n # Build client config\n client_config = {\"api_key\": api_key}\n \n # Add base URL if needed\n if base_url := defaults.get(\"base_url\"):\n client_config[\"base_url\"] = base_url\n \n return client_config, model\n\n\n@dataclass\nclass ChunkingConfig:\n \"\"\"Configuration for conversation chunking\"\"\"\n strategy: ChunkingStrategy = ChunkingStrategy.FIXED_ROUNDS\n rounds_per_chunk: int = 20 # Number of rounds per chunk for FIXED_ROUNDS\n overlap_rounds: int = 2 # Number of overlapping rounds between chunks\n include_metadata: bool = True # Include conversation metadata in chunks\n min_chunk_size: int = 5 # Minimum number of rounds in a chunk\n max_chunk_size: int = 50 # Maximum number of rounds in a chunk\n\n\n@dataclass\nclass IndexConfig:\n \"\"\"Configuration for RAG indexing\"\"\"\n mode: IndexMode = IndexMode.HYBRID\n embedding_model: str = \"text-embedding-3-small\" # OpenAI embedding model\n embedding_dim: int = 1536 # Dimension of embeddings\n index_path: str = \"indexes/memory_index\"\n chunk_store_path: str = \"data/chunk_store.json\"\n enable_contextual: bool = True # Add contextual information to chunks\n contextual_window: int = 2 # Number of surrounding rounds for context\n\n\n@dataclass\nclass EvaluationConfig:\n \"\"\"Configuration for evaluation framework\"\"\"\n test_cases_dir: str = \"../../week2/user-memory-evaluation/test_cases\"\n results_dir: str = \"results\"\n enable_verbose: bool = True\n save_trajectories: bool = True\n max_iterations: int = 10 # Max iterations for ReAct pattern\n enable_caching: bool = True # Cache indexed conversations\n use_llm_judge: bool = False # Use LLM to evaluate answers\n\n\n@dataclass\nclass AgentConfig:\n \"\"\"Agent behavior configuration\"\"\"\n enable_reasoning: bool = True # Show reasoning steps\n enable_citations: bool = True # Include citations in responses\n max_search_results: int = 5 # Maximum search results to consider\n confidence_threshold: float = 0.7 # Minimum confidence for answers\n enable_multi_search: bool = True # Allow multiple searches per query\n max_searches_per_query: int = 3 # Maximum searches allowed\n verbose: bool = True # Enable verbose output\n\n\n@dataclass\nclass Config:\n \"\"\"Main configuration container\"\"\"\n llm: LLMConfig = field(default_factory=LLMConfig)\n chunking: ChunkingConfig = field(default_factory=ChunkingConfig)\n index: IndexConfig = field(default_factory=IndexConfig)\n evaluation: EvaluationConfig = field(default_factory=EvaluationConfig)\n agent: AgentConfig = field(default_factory=AgentConfig)\n \n @classmethod\n def from_env(cls) -> \"Config\":\n \"\"\"Create configuration from environment variables\"\"\"\n config = cls()\n \n # Override with environment variables\n if provider := os.getenv(\"LLM_PROVIDER\"):\n config.llm.provider = provider\n \n if model := os.getenv(\"LLM_MODEL\"):\n config.llm.model = model\n \n if rounds := os.getenv(\"ROUNDS_PER_CHUNK\"):\n config.chunking.rounds_per_chunk = int(rounds)\n \n if index_mode := os.getenv(\"INDEX_MODE\"):\n config.index.mode = IndexMode(index_mode)\n \n return config\n \n def save(self, path: str):\n \"\"\"Save configuration to JSON file\"\"\"\n import json\n \n config_dict = {\n \"llm\": {\n \"provider\": self.llm.provider,\n \"model\": self.llm.model,\n \"temperature\": _reasoning_safe_temperature(self.llm.model, self.llm.temperature),\n \"max_tokens\": self.llm.max_tokens,\n \"stream\": self.llm.stream\n },\n \"chunking\": {\n \"strategy\": self.chunking.strategy,\n \"rounds_per_chunk\": self.chunking.rounds_per_chunk,\n \"overlap_rounds\": self.chunking.overlap_rounds,\n \"include_metadata\": self.chunking.include_metadata\n },\n \"index\": {\n \"mode\": self.index.mode,\n \"embedding_model\": self.index.embedding_model,\n \"enable_contextual\": self.index.enable_contextual,\n \"contextual_window\": self.index.contextual_window\n },\n \"evaluation\": {\n \"enable_verbose\": self.evaluation.enable_verbose,\n \"save_trajectories\": self.evaluation.save_trajectories,\n \"max_iterations\": self.evaluation.max_iterations\n },\n \"agent\": {\n \"enable_reasoning\": self.agent.enable_reasoning,\n \"enable_citations\": self.agent.enable_citations,\n \"max_search_results\": self.agent.max_search_results,\n \"confidence_threshold\": self.agent.confidence_threshold\n }\n }\n \n with open(path, 'w') as f:\n json.dump(config_dict, f, indent=2)\n \n @classmethod\n def load(cls, path: str) -> \"Config\":\n \"\"\"Load configuration from JSON file\"\"\"\n import json\n \n with open(path, 'r') as f:\n config_dict = json.load(f)\n \n config = cls()\n \n # Update LLM config\n if \"llm\" in config_dict:\n for key, value in config_dict[\"llm\"].items():\n setattr(config.llm, key, value)\n \n # Update other configs similarly\n for section in [\"chunking\", \"index\", \"evaluation\", \"agent\"]:\n if section in config_dict:\n section_config = getattr(config, section)\n for key, value in config_dict[section].items():\n # Handle enums\n if key == \"strategy\" and section == \"chunking\":\n value = ChunkingStrategy(value)\n elif key == \"mode\" and section == \"index\":\n value = IndexMode(value)\n setattr(section_config, key, value)\n \n return config\n"}, "30448a58168491cec2efbbbd82222969bdd65a7b": {"t": 1785766443.2609553, "n": 10937, "h": "98db21a54f80b9c6", "c": "\"\"\"Configuration for Agentic RAG User Memory Evaluation System\"\"\"\n\nimport os\nfrom dataclasses import dataclass, field\nfrom typing import Optional, Dict, Any, List\nfrom enum import Enum\nfrom pathlib import Path\n\n\ndef _reasoning_safe_temperature(model, requested=1.0):\n \"\"\"Reasoning models (Kimi K3, GPT-5, ...) only accept temperature=1.\n Return 1 for those; otherwise the requested value so non-reasoning\n providers (Doubao, DeepSeek, older Moonshot) are unchanged.\"\"\"\n m = str(model or \"\").lower().replace(\"/\", \"-\")\n return 1 if (\"kimi-k3\" in m or \"gpt-5\" in m) else requested\n\n\ndef _openrouter_model_id(model: Optional[str]) -> str:\n \"\"\"Map a provider-native model name to an OpenRouter model id, used by the\n universal OpenRouter fallback. An explicit OPENROUTER_MODEL env var wins.\"\"\"\n override = os.getenv(\"OPENROUTER_MODEL\")\n if override:\n return override\n m = (model or \"\").strip()\n if not m:\n return \"openai/gpt-5.6-luna\"\n if \"/\" in m:\n return m # already an OpenRouter-style id (e.g. openai/gpt-4o)\n ml = m.lower()\n if ml.startswith((\"gpt-\", \"o1\", \"o3\", \"o4\", \"chatgpt\")):\n return \"openai/\" + m\n if ml.startswith(\"claude-\"):\n return \"anthropic/claude-opus-4.8\"\n if ml.startswith(\"kimi\"):\n # kimi-k3 is not on OpenRouter; moonshotai/kimi-k2.6 is the closest hosted id.\n return \"moonshotai/kimi-k2.6\"\n # Provider-native ids (kimi-*/doubao-*/qwen/deepseek-*) not hosted on\n # OpenRouter under the same name -> a widely-available OpenAI chat model.\n return \"openai/gpt-5.6-luna\"\n\n\nclass Provider(str, Enum):\n \"\"\"Supported LLM providers\"\"\"\n SILICONFLOW = \"siliconflow\"\n DOUBAO = \"doubao\"\n KIMI = \"kimi\"\n MOONSHOT = \"moonshot\"\n OPENROUTER = \"openrouter\"\n OPENAI = \"openai\"\n GROQ = \"groq\"\n TOGETHER = \"together\"\n DEEPSEEK = \"deepseek\"\n\n\nclass IndexMode(str, Enum):\n \"\"\"Indexing modes for conversation chunks\"\"\"\n DENSE = \"dense\" # Dense embedding only\n SPARSE = \"sparse\" # Sparse embedding only (BM25)\n HYBRID = \"hybrid\" # Both dense and sparse\n\n\nclass ChunkingStrategy(str, Enum):\n \"\"\"Strategies for chunking conversations\"\"\"\n FIXED_ROUNDS = \"fixed_rounds\" # Fixed number of rounds per chunk\n SEMANTIC = \"semantic\" # Semantic boundaries\n TIME_BASED = \"time_based\" # Based on timestamp gaps\n\n\n@dataclass\nclass LLMConfig:\n \"\"\"LLM configuration\"\"\"\n provider: str = \"kimi\" # Default provider\n model: Optional[str] = None # Will use provider defaults if not specified\n api_key: Optional[str] = None # Will read from env if not provided\n temperature: float = 0.7\n max_tokens: int = 2048\n stream: bool = True\n \n # Provider-specific defaults\n PROVIDER_DEFAULTS = {\n \"siliconflow\": {\n \"model\": \"Qwen/Qwen3-235B-A22B-Thinking-2507\",\n \"base_url\": \"https://api.siliconflow.cn/v1\"\n },\n \"doubao\": {\n \"model\": \"doubao-seed-1-6-thinking-250715\",\n \"base_url\": \"https://ark.cn-beijing.volces.com/api/v3\"\n },\n \"kimi\": {\n \"model\": \"kimi-k3\",\n \"base_url\": \"https://api.moonshot.cn/v1\"\n },\n \"moonshot\": {\n \"model\": \"kimi-k3\",\n \"base_url\": \"https://api.moonshot.cn/v1\"\n },\n \"openrouter\": {\n \"model\": \"openai/gpt-4o-2024-11-20\",\n \"base_url\": \"https://openrouter.ai/api/v1\"\n },\n \"openai\": {\n \"model\": \"gpt-4o-2024-11-20\",\n \"base_url\": \"https://api.openai.com/v1\"\n },\n \"groq\": {\n \"model\": \"llama-3.3-70b-versatile\",\n \"base_url\": \"https://api.groq.com/openai/v1\"\n },\n \"together\": {\n \"model\": \"meta-llama/Llama-3.3-70B-Instruct-Turbo\",\n \"base_url\": \"https://api.together.xyz\"\n },\n \"deepseek\": {\n \"model\": \"deepseek-reasoner\",\n \"base_url\": \"https://api.deepseek.com/v1\"\n }\n }\n \n def get_client_config(self) -> tuple[Dict[str, Any], str]:\n \"\"\"Get OpenAI client configuration\"\"\"\n provider = self.provider.lower()\n defaults = self.PROVIDER_DEFAULTS.get(provider, {})\n \n # Determine API key\n api_key = self.api_key or os.getenv(f\"{provider.upper()}_API_KEY\")\n if not api_key and provider == \"moonshot\":\n api_key = os.getenv(\"KIMI_API_KEY\") # Fallback for moonshot\n\n # Determine model\n model = self.model or defaults.get(\"model\", \"gpt-4o\")\n\n # Universal OpenRouter fallback: primary provider key absent but\n # OPENROUTER_API_KEY present -> route through OpenRouter.\n if not api_key and provider != \"openrouter\" and os.getenv(\"OPENROUTER_API_KEY\"):\n return {\n \"api_key\": os.getenv(\"OPENROUTER_API_KEY\"),\n \"base_url\": \"https://openrouter.ai/api/v1\",\n }, _openrouter_model_id(model)\n\n # Build client config\n client_config = {\"api_key\": api_key}\n \n # Add base URL if needed\n if base_url := defaults.get(\"base_url\"):\n client_config[\"base_url\"] = base_url\n \n return client_config, model\n\n\n@dataclass\nclass ChunkingConfig:\n \"\"\"Configuration for conversation chunking\"\"\"\n strategy: ChunkingStrategy = ChunkingStrategy.FIXED_ROUNDS\n rounds_per_chunk: int = 20 # Number of rounds per chunk for FIXED_ROUNDS\n overlap_rounds: int = 2 # Number of overlapping rounds between chunks\n include_metadata: bool = True # Include conversation metadata in chunks\n min_chunk_size: int = 5 # Minimum number of rounds in a chunk\n max_chunk_size: int = 50 # Maximum number of rounds in a chunk\n\n\n@dataclass\nclass IndexConfig:\n \"\"\"Configuration for RAG indexing\"\"\"\n mode: IndexMode = IndexMode.HYBRID\n embedding_model: str = \"text-embedding-3-small\" # OpenAI embedding model\n embedding_dim: int = 1536 # Dimension of embeddings\n index_path: str = \"indexes/memory_index\"\n chunk_store_path: str = \"data/chunk_store.json\"\n enable_contextual: bool = True # Add contextual information to chunks\n contextual_window: int = 2 # Number of surrounding rounds for context\n\n\n@dataclass\nclass EvaluationConfig:\n \"\"\"Configuration for evaluation framework\"\"\"\n test_cases_dir: str = \"../../week2/user-memory-evaluation/test_cases\"\n results_dir: str = \"results\"\n enable_verbose: bool = True\n save_trajectories: bool = True\n max_iterations: int = 10 # Max iterations for ReAct pattern\n enable_caching: bool = True # Cache indexed conversations\n use_llm_judge: bool = False # Use LLM to evaluate answers\n\n\n@dataclass\nclass AgentConfig:\n \"\"\"Agent behavior configuration\"\"\"\n enable_reasoning: bool = True # Show reasoning steps\n enable_citations: bool = True # Include citations in responses\n max_search_results: int = 5 # Maximum search results to consider\n confidence_threshold: float = 0.7 # Minimum confidence for answers\n enable_multi_search: bool = True # Allow multiple searches per query\n max_searches_per_query: int = 3 # Maximum searches allowed\n verbose: bool = True # Enable verbose output\n\n\n@dataclass\nclass Config:\n \"\"\"Main configuration container\"\"\"\n llm: LLMConfig = field(default_factory=LLMConfig)\n chunking: ChunkingConfig = field(default_factory=ChunkingConfig)\n index: IndexConfig = field(default_factory=IndexConfig)\n evaluation: EvaluationConfig = field(default_factory=EvaluationConfig)\n agent: AgentConfig = field(default_factory=AgentConfig)\n \n @classmethod\n def from_env(cls) -> \"Config\":\n \"\"\"Create configuration from environment variables\"\"\"\n config = cls()\n \n # Override with environment variables\n if provider := os.getenv(\"LLM_PROVIDER\"):\n config.llm.provider = provider\n \n if model := os.getenv(\"LLM_MODEL\"):\n config.llm.model = model\n \n if rounds := os.getenv(\"ROUNDS_PER_CHUNK\"):\n config.chunking.rounds_per_chunk = int(rounds)\n \n if index_mode := os.getenv(\"INDEX_MODE\"):\n config.index.mode = IndexMode(index_mode)\n \n return config\n \n def save(self, path: str):\n \"\"\"Save configuration to JSON file\"\"\"\n import json\n \n config_dict = {\n \"llm\": {\n \"provider\": self.llm.provider,\n \"model\": self.llm.model,\n \"temperature\": _reasoning_safe_temperature(self.llm.model, self.llm.temperature),\n \"max_tokens\": self.llm.max_tokens,\n \"stream\": self.llm.stream\n },\n \"chunking\": {\n \"strategy\": self.chunking.strategy,\n \"rounds_per_chunk\": self.chunking.rounds_per_chunk,\n \"overlap_rounds\": self.chunking.overlap_rounds,\n \"include_metadata\": self.chunking.include_metadata\n },\n \"index\": {\n \"mode\": self.index.mode,\n \"embedding_model\": self.index.embedding_model,\n \"enable_contextual\": self.index.enable_contextual,\n \"contextual_window\": self.index.contextual_window\n },\n \"evaluation\": {\n \"enable_verbose\": self.evaluation.enable_verbose,\n \"save_trajectories\": self.evaluation.save_trajectories,\n \"max_iterations\": self.evaluation.max_iterations\n },\n \"agent\": {\n \"enable_reasoning\": self.agent.enable_reasoning,\n \"enable_citations\": self.agent.enable_citations,\n \"max_search_results\": self.agent.max_search_results,\n \"confidence_threshold\": self.agent.confidence_threshold\n }\n }\n \n with open(path, 'w') as f:\n json.dump(config_dict, f, indent=2)\n \n @classmethod\n def load(cls, path: str) -> \"Config\":\n \"\"\"Load configuration from JSON file\"\"\"\n import json\n \n with open(path, 'r') as f:\n config_dict = json.load(f)\n \n config = cls()\n \n # Update LLM config\n if \"llm\" in config_dict:\n for key, value in config_dict[\"llm\"].items():\n setattr(config.llm, key, value)\n \n # Update other configs similarly\n for section in [\"chunking\", \"index\", \"evaluation\", \"agent\"]:\n if section in config_dict:\n section_config = getattr(config, section)\n for key, value in config_dict[section].items():\n # Handle enums\n if key == \"strategy\" and section == \"chunking\":\n value = ChunkingStrategy(value)\n elif key == \"mode\" and section == \"index\":\n value = IndexMode(value)\n setattr(section_config, key, value)\n \n return config\n"}, "e2a9aeeb0a358168d8963a78a7903153903d27e1": {"t": 1785766444.8762555, "n": 10780, "h": "122ad4ba488ff90e", "c": "\"\"\"Configuration for Agentic RAG User Memory Evaluation System\"\"\"\n\nimport os\nfrom dataclasses import dataclass, field\nfrom typing import Optional, Dict, Any, List\nfrom enum import Enum\nfrom pathlib import Path\n\n\ndef _reasoning_safe_temperature(model, requested=1.0):\n \"\"\"Reasoning models (Kimi K3, GPT-5, ...) only accept temperature=1.\n Return 1 for those; otherwise the requested value so non-reasoning\n providers (Doubao, DeepSeek, older Moonshot) are unchanged.\"\"\"\n m = str(model or \"\").lower().replace(\"/\", \"-\")\n return 1 if (\"kimi-k3\" in m or \"gpt-5\" in m) else requested\n\n\ndef _openrouter_model_id(model: Optional[str]) -> str:\n \"\"\"Map a provider-native model name to an OpenRouter model id, used by the\n universal OpenRouter fallback. An explicit OPENROUTER_MODEL env var wins.\"\"\"\n override = os.getenv(\"OPENROUTER_MODEL\")\n if override:\n return override\n m = (model or \"\").strip()\n if not m:\n return \"openai/gpt-4o-mini\"\n if \"/\" in m:\n return m # already an OpenRouter-style id (e.g. openai/gpt-4o)\n ml = m.lower()\n if ml.startswith((\"gpt-\", \"o1\", \"o3\", \"o4\", \"chatgpt\")):\n return \"openai/\" + m\n if ml.startswith(\"claude-\"):\n return \"anthropic/claude-opus-4.8\"\n # Provider-native ids (kimi-*/doubao-*/qwen/deepseek-*) not hosted on\n # OpenRouter under the same name -> a widely-available OpenAI chat model.\n return \"openai/gpt-4o-mini\"\n\n\nclass Provider(str, Enum):\n \"\"\"Supported LLM providers\"\"\"\n SILICONFLOW = \"siliconflow\"\n DOUBAO = \"doubao\"\n KIMI = \"kimi\"\n MOONSHOT = \"moonshot\"\n OPENROUTER = \"openrouter\"\n OPENAI = \"openai\"\n GROQ = \"groq\"\n TOGETHER = \"together\"\n DEEPSEEK = \"deepseek\"\n\n\nclass IndexMode(str, Enum):\n \"\"\"Indexing modes for conversation chunks\"\"\"\n DENSE = \"dense\" # Dense embedding only\n SPARSE = \"sparse\" # Sparse embedding only (BM25)\n HYBRID = \"hybrid\" # Both dense and sparse\n\n\nclass ChunkingStrategy(str, Enum):\n \"\"\"Strategies for chunking conversations\"\"\"\n FIXED_ROUNDS = \"fixed_rounds\" # Fixed number of rounds per chunk\n SEMANTIC = \"semantic\" # Semantic boundaries\n TIME_BASED = \"time_based\" # Based on timestamp gaps\n\n\n@dataclass\nclass LLMConfig:\n \"\"\"LLM configuration\"\"\"\n provider: str = \"kimi\" # Default provider\n model: Optional[str] = None # Will use provider defaults if not specified\n api_key: Optional[str] = None # Will read from env if not provided\n temperature: float = 0.7\n max_tokens: int = 2048\n stream: bool = True\n \n # Provider-specific defaults\n PROVIDER_DEFAULTS = {\n \"siliconflow\": {\n \"model\": \"Qwen/Qwen3-235B-A22B-Thinking-2507\",\n \"base_url\": \"https://api.siliconflow.cn/v1\"\n },\n \"doubao\": {\n \"model\": \"doubao-seed-1-6-thinking-250715\",\n \"base_url\": \"https://ark.cn-beijing.volces.com/api/v3\"\n },\n \"kimi\": {\n \"model\": \"kimi-k3\",\n \"base_url\": \"https://api.moonshot.cn/v1\"\n },\n \"moonshot\": {\n \"model\": \"kimi-k3\",\n \"base_url\": \"https://api.moonshot.cn/v1\"\n },\n \"openrouter\": {\n \"model\": \"openai/gpt-4o-2024-11-20\",\n \"base_url\": \"https://openrouter.ai/api/v1\"\n },\n \"openai\": {\n \"model\": \"gpt-4o-2024-11-20\",\n \"base_url\": \"https://api.openai.com/v1\"\n },\n \"groq\": {\n \"model\": \"llama-3.3-70b-versatile\",\n \"base_url\": \"https://api.groq.com/openai/v1\"\n },\n \"together\": {\n \"model\": \"meta-llama/Llama-3.3-70B-Instruct-Turbo\",\n \"base_url\": \"https://api.together.xyz\"\n },\n \"deepseek\": {\n \"model\": \"deepseek-reasoner\",\n \"base_url\": \"https://api.deepseek.com/v1\"\n }\n }\n \n def get_client_config(self) -> tuple[Dict[str, Any], str]:\n \"\"\"Get OpenAI client configuration\"\"\"\n provider = self.provider.lower()\n defaults = self.PROVIDER_DEFAULTS.get(provider, {})\n \n # Determine API key\n api_key = self.api_key or os.getenv(f\"{provider.upper()}_API_KEY\")\n if not api_key and provider == \"moonshot\":\n api_key = os.getenv(\"KIMI_API_KEY\") # Fallback for moonshot\n\n # Determine model\n model = self.model or defaults.get(\"model\", \"gpt-4o\")\n\n # Universal OpenRouter fallback: primary provider key absent but\n # OPENROUTER_API_KEY present -> route through OpenRouter.\n if not api_key and provider != \"openrouter\" and os.getenv(\"OPENROUTER_API_KEY\"):\n return {\n \"api_key\": os.getenv(\"OPENROUTER_API_KEY\"),\n \"base_url\": \"https://openrouter.ai/api/v1\",\n }, _openrouter_model_id(model)\n\n # Build client config\n client_config = {\"api_key\": api_key}\n \n # Add base URL if needed\n if base_url := defaults.get(\"base_url\"):\n client_config[\"base_url\"] = base_url\n \n return client_config, model\n\n\n@dataclass\nclass ChunkingConfig:\n \"\"\"Configuration for conversation chunking\"\"\"\n strategy: ChunkingStrategy = ChunkingStrategy.FIXED_ROUNDS\n rounds_per_chunk: int = 20 # Number of rounds per chunk for FIXED_ROUNDS\n overlap_rounds: int = 2 # Number of overlapping rounds between chunks\n include_metadata: bool = True # Include conversation metadata in chunks\n min_chunk_size: int = 5 # Minimum number of rounds in a chunk\n max_chunk_size: int = 50 # Maximum number of rounds in a chunk\n\n\n@dataclass\nclass IndexConfig:\n \"\"\"Configuration for RAG indexing\"\"\"\n mode: IndexMode = IndexMode.HYBRID\n embedding_model: str = \"text-embedding-3-small\" # OpenAI embedding model\n embedding_dim: int = 1536 # Dimension of embeddings\n index_path: str = \"indexes/memory_index\"\n chunk_store_path: str = \"data/chunk_store.json\"\n enable_contextual: bool = True # Add contextual information to chunks\n contextual_window: int = 2 # Number of surrounding rounds for context\n\n\n@dataclass\nclass EvaluationConfig:\n \"\"\"Configuration for evaluation framework\"\"\"\n test_cases_dir: str = \"../../week2/user-memory-evaluation/test_cases\"\n results_dir: str = \"results\"\n enable_verbose: bool = True\n save_trajectories: bool = True\n max_iterations: int = 10 # Max iterations for ReAct pattern\n enable_caching: bool = True # Cache indexed conversations\n use_llm_judge: bool = False # Use LLM to evaluate answers\n\n\n@dataclass\nclass AgentConfig:\n \"\"\"Agent behavior configuration\"\"\"\n enable_reasoning: bool = True # Show reasoning steps\n enable_citations: bool = True # Include citations in responses\n max_search_results: int = 5 # Maximum search results to consider\n confidence_threshold: float = 0.7 # Minimum confidence for answers\n enable_multi_search: bool = True # Allow multiple searches per query\n max_searches_per_query: int = 3 # Maximum searches allowed\n verbose: bool = True # Enable verbose output\n\n\n@dataclass\nclass Config:\n \"\"\"Main configuration container\"\"\"\n llm: LLMConfig = field(default_factory=LLMConfig)\n chunking: ChunkingConfig = field(default_factory=ChunkingConfig)\n index: IndexConfig = field(default_factory=IndexConfig)\n evaluation: EvaluationConfig = field(default_factory=EvaluationConfig)\n agent: AgentConfig = field(default_factory=AgentConfig)\n \n @classmethod\n def from_env(cls) -> \"Config\":\n \"\"\"Create configuration from environment variables\"\"\"\n config = cls()\n \n # Override with environment variables\n if provider := os.getenv(\"LLM_PROVIDER\"):\n config.llm.provider = provider\n \n if model := os.getenv(\"LLM_MODEL\"):\n config.llm.model = model\n \n if rounds := os.getenv(\"ROUNDS_PER_CHUNK\"):\n config.chunking.rounds_per_chunk = int(rounds)\n \n if index_mode := os.getenv(\"INDEX_MODE\"):\n config.index.mode = IndexMode(index_mode)\n \n return config\n \n def save(self, path: str):\n \"\"\"Save configuration to JSON file\"\"\"\n import json\n \n config_dict = {\n \"llm\": {\n \"provider\": self.llm.provider,\n \"model\": self.llm.model,\n \"temperature\": _reasoning_safe_temperature(self.llm.model, self.llm.temperature),\n \"max_tokens\": self.llm.max_tokens,\n \"stream\": self.llm.stream\n },\n \"chunking\": {\n \"strategy\": self.chunking.strategy,\n \"rounds_per_chunk\": self.chunking.rounds_per_chunk,\n \"overlap_rounds\": self.chunking.overlap_rounds,\n \"include_metadata\": self.chunking.include_metadata\n },\n \"index\": {\n \"mode\": self.index.mode,\n \"embedding_model\": self.index.embedding_model,\n \"enable_contextual\": self.index.enable_contextual,\n \"contextual_window\": self.index.contextual_window\n },\n \"evaluation\": {\n \"enable_verbose\": self.evaluation.enable_verbose,\n \"save_trajectories\": self.evaluation.save_trajectories,\n \"max_iterations\": self.evaluation.max_iterations\n },\n \"agent\": {\n \"enable_reasoning\": self.agent.enable_reasoning,\n \"enable_citations\": self.agent.enable_citations,\n \"max_search_results\": self.agent.max_search_results,\n \"confidence_threshold\": self.agent.confidence_threshold\n }\n }\n \n with open(path, 'w') as f:\n json.dump(config_dict, f, indent=2)\n \n @classmethod\n def load(cls, path: str) -> \"Config\":\n \"\"\"Load configuration from JSON file\"\"\"\n import json\n \n with open(path, 'r') as f:\n config_dict = json.load(f)\n \n config = cls()\n \n # Update LLM config\n if \"llm\" in config_dict:\n for key, value in config_dict[\"llm\"].items():\n setattr(config.llm, key, value)\n \n # Update other configs similarly\n for section in [\"chunking\", \"index\", \"evaluation\", \"agent\"]:\n if section in config_dict:\n section_config = getattr(config, section)\n for key, value in config_dict[section].items():\n # Handle enums\n if key == \"strategy\" and section == \"chunking\":\n value = ChunkingStrategy(value)\n elif key == \"mode\" and section == \"index\":\n value = IndexMode(value)\n setattr(section_config, key, value)\n \n return config\n"}, "033b80f08b2313abdc34f236d3e023986dd84828": {"t": 1785766447.0238924, "n": 9542, "h": "3fae0dee2342d9da", "c": "\"\"\"Configuration for Agentic RAG User Memory Evaluation System\"\"\"\n\nimport os\nfrom dataclasses import dataclass, field\nfrom typing import Optional, Dict, Any, List\nfrom enum import Enum\nfrom pathlib import Path\n\n\ndef _reasoning_safe_temperature(model, requested=1.0):\n \"\"\"Reasoning models (Kimi K3, GPT-5, ...) only accept temperature=1.\n Return 1 for those; otherwise the requested value so non-reasoning\n providers (Doubao, DeepSeek, older Moonshot) are unchanged.\"\"\"\n m = str(model or \"\").lower().replace(\"/\", \"-\")\n return 1 if (\"kimi-k3\" in m or \"gpt-5\" in m) else requested\n\n\nclass Provider(str, Enum):\n \"\"\"Supported LLM providers\"\"\"\n SILICONFLOW = \"siliconflow\"\n DOUBAO = \"doubao\"\n KIMI = \"kimi\"\n MOONSHOT = \"moonshot\"\n OPENROUTER = \"openrouter\"\n OPENAI = \"openai\"\n GROQ = \"groq\"\n TOGETHER = \"together\"\n DEEPSEEK = \"deepseek\"\n\n\nclass IndexMode(str, Enum):\n \"\"\"Indexing modes for conversation chunks\"\"\"\n DENSE = \"dense\" # Dense embedding only\n SPARSE = \"sparse\" # Sparse embedding only (BM25)\n HYBRID = \"hybrid\" # Both dense and sparse\n\n\nclass ChunkingStrategy(str, Enum):\n \"\"\"Strategies for chunking conversations\"\"\"\n FIXED_ROUNDS = \"fixed_rounds\" # Fixed number of rounds per chunk\n SEMANTIC = \"semantic\" # Semantic boundaries\n TIME_BASED = \"time_based\" # Based on timestamp gaps\n\n\n@dataclass\nclass LLMConfig:\n \"\"\"LLM configuration\"\"\"\n provider: str = \"kimi\" # Default provider\n model: Optional[str] = None # Will use provider defaults if not specified\n api_key: Optional[str] = None # Will read from env if not provided\n temperature: float = 0.7\n max_tokens: int = 2048\n stream: bool = True\n \n # Provider-specific defaults\n PROVIDER_DEFAULTS = {\n \"siliconflow\": {\n \"model\": \"Qwen/Qwen3-235B-A22B-Thinking-2507\",\n \"base_url\": \"https://api.siliconflow.cn/v1\"\n },\n \"doubao\": {\n \"model\": \"doubao-seed-1-6-thinking-250715\",\n \"base_url\": \"https://ark.cn-beijing.volces.com/api/v3\"\n },\n \"kimi\": {\n \"model\": \"kimi-k3\",\n \"base_url\": \"https://api.moonshot.cn/v1\"\n },\n \"moonshot\": {\n \"model\": \"kimi-k3\",\n \"base_url\": \"https://api.moonshot.cn/v1\"\n },\n \"openrouter\": {\n \"model\": \"openai/gpt-4o-2024-11-20\",\n \"base_url\": \"https://openrouter.ai/api/v1\"\n },\n \"openai\": {\n \"model\": \"gpt-4o-2024-11-20\",\n \"base_url\": \"https://api.openai.com/v1\"\n },\n \"groq\": {\n \"model\": \"llama-3.3-70b-versatile\",\n \"base_url\": \"https://api.groq.com/openai/v1\"\n },\n \"together\": {\n \"model\": \"meta-llama/Llama-3.3-70B-Instruct-Turbo\",\n \"base_url\": \"https://api.together.xyz\"\n },\n \"deepseek\": {\n \"model\": \"deepseek-reasoner\",\n \"base_url\": \"https://api.deepseek.com/v1\"\n }\n }\n \n def get_client_config(self) -> tuple[Dict[str, Any], str]:\n \"\"\"Get OpenAI client configuration\"\"\"\n provider = self.provider.lower()\n defaults = self.PROVIDER_DEFAULTS.get(provider, {})\n \n # Determine API key\n api_key = self.api_key or os.getenv(f\"{provider.upper()}_API_KEY\")\n if not api_key and provider == \"moonshot\":\n api_key = os.getenv(\"KIMI_API_KEY\") # Fallback for moonshot\n \n # Determine model\n model = self.model or defaults.get(\"model\", \"gpt-4o\")\n \n # Build client config\n client_config = {\"api_key\": api_key}\n \n # Add base URL if needed\n if base_url := defaults.get(\"base_url\"):\n client_config[\"base_url\"] = base_url\n \n return client_config, model\n\n\n@dataclass\nclass ChunkingConfig:\n \"\"\"Configuration for conversation chunking\"\"\"\n strategy: ChunkingStrategy = ChunkingStrategy.FIXED_ROUNDS\n rounds_per_chunk: int = 20 # Number of rounds per chunk for FIXED_ROUNDS\n overlap_rounds: int = 2 # Number of overlapping rounds between chunks\n include_metadata: bool = True # Include conversation metadata in chunks\n min_chunk_size: int = 5 # Minimum number of rounds in a chunk\n max_chunk_size: int = 50 # Maximum number of rounds in a chunk\n\n\n@dataclass\nclass IndexConfig:\n \"\"\"Configuration for RAG indexing\"\"\"\n mode: IndexMode = IndexMode.HYBRID\n embedding_model: str = \"text-embedding-3-small\" # OpenAI embedding model\n embedding_dim: int = 1536 # Dimension of embeddings\n index_path: str = \"indexes/memory_index\"\n chunk_store_path: str = \"data/chunk_store.json\"\n enable_contextual: bool = True # Add contextual information to chunks\n contextual_window: int = 2 # Number of surrounding rounds for context\n\n\n@dataclass\nclass EvaluationConfig:\n \"\"\"Configuration for evaluation framework\"\"\"\n test_cases_dir: str = \"../../week2/user-memory-evaluation/test_cases\"\n results_dir: str = \"results\"\n enable_verbose: bool = True\n save_trajectories: bool = True\n max_iterations: int = 10 # Max iterations for ReAct pattern\n enable_caching: bool = True # Cache indexed conversations\n use_llm_judge: bool = False # Use LLM to evaluate answers\n\n\n@dataclass\nclass AgentConfig:\n \"\"\"Agent behavior configuration\"\"\"\n enable_reasoning: bool = True # Show reasoning steps\n enable_citations: bool = True # Include citations in responses\n max_search_results: int = 5 # Maximum search results to consider\n confidence_threshold: float = 0.7 # Minimum confidence for answers\n enable_multi_search: bool = True # Allow multiple searches per query\n max_searches_per_query: int = 3 # Maximum searches allowed\n verbose: bool = True # Enable verbose output\n\n\n@dataclass\nclass Config:\n \"\"\"Main configuration container\"\"\"\n llm: LLMConfig = field(default_factory=LLMConfig)\n chunking: ChunkingConfig = field(default_factory=ChunkingConfig)\n index: IndexConfig = field(default_factory=IndexConfig)\n evaluation: EvaluationConfig = field(default_factory=EvaluationConfig)\n agent: AgentConfig = field(default_factory=AgentConfig)\n \n @classmethod\n def from_env(cls) -> \"Config\":\n \"\"\"Create configuration from environment variables\"\"\"\n config = cls()\n \n # Override with environment variables\n if provider := os.getenv(\"LLM_PROVIDER\"):\n config.llm.provider = provider\n \n if model := os.getenv(\"LLM_MODEL\"):\n config.llm.model = model\n \n if rounds := os.getenv(\"ROUNDS_PER_CHUNK\"):\n config.chunking.rounds_per_chunk = int(rounds)\n \n if index_mode := os.getenv(\"INDEX_MODE\"):\n config.index.mode = IndexMode(index_mode)\n \n return config\n \n def save(self, path: str):\n \"\"\"Save configuration to JSON file\"\"\"\n import json\n \n config_dict = {\n \"llm\": {\n \"provider\": self.llm.provider,\n \"model\": self.llm.model,\n \"temperature\": _reasoning_safe_temperature(self.llm.model, self.llm.temperature),\n \"max_tokens\": self.llm.max_tokens,\n \"stream\": self.llm.stream\n },\n \"chunking\": {\n \"strategy\": self.chunking.strategy,\n \"rounds_per_chunk\": self.chunking.rounds_per_chunk,\n \"overlap_rounds\": self.chunking.overlap_rounds,\n \"include_metadata\": self.chunking.include_metadata\n },\n \"index\": {\n \"mode\": self.index.mode,\n \"embedding_model\": self.index.embedding_model,\n \"enable_contextual\": self.index.enable_contextual,\n \"contextual_window\": self.index.contextual_window\n },\n \"evaluation\": {\n \"enable_verbose\": self.evaluation.enable_verbose,\n \"save_trajectories\": self.evaluation.save_trajectories,\n \"max_iterations\": self.evaluation.max_iterations\n },\n \"agent\": {\n \"enable_reasoning\": self.agent.enable_reasoning,\n \"enable_citations\": self.agent.enable_citations,\n \"max_search_results\": self.agent.max_search_results,\n \"confidence_threshold\": self.agent.confidence_threshold\n }\n }\n \n with open(path, 'w') as f:\n json.dump(config_dict, f, indent=2)\n \n @classmethod\n def load(cls, path: str) -> \"Config\":\n \"\"\"Load configuration from JSON file\"\"\"\n import json\n \n with open(path, 'r') as f:\n config_dict = json.load(f)\n \n config = cls()\n \n # Update LLM config\n if \"llm\" in config_dict:\n for key, value in config_dict[\"llm\"].items():\n setattr(config.llm, key, value)\n \n # Update other configs similarly\n for section in [\"chunking\", \"index\", \"evaluation\", \"agent\"]:\n if section in config_dict:\n section_config = getattr(config, section)\n for key, value in config_dict[section].items():\n # Handle enums\n if key == \"strategy\" and section == \"chunking\":\n value = ChunkingStrategy(value)\n elif key == \"mode\" and section == \"index\":\n value = IndexMode(value)\n setattr(section_config, key, value)\n \n return config\n"}, "1de75c9d0283e0d2fbf840de748eb47da143ec52": {"t": 1785766448.4155443, "n": 9140, "h": "422ca5dd1d94b3a6", "c": "\"\"\"Configuration for Agentic RAG User Memory Evaluation System\"\"\"\n\nimport os\nfrom dataclasses import dataclass, field\nfrom typing import Optional, Dict, Any, List\nfrom enum import Enum\nfrom pathlib import Path\n\n\nclass Provider(str, Enum):\n \"\"\"Supported LLM providers\"\"\"\n SILICONFLOW = \"siliconflow\"\n DOUBAO = \"doubao\"\n KIMI = \"kimi\"\n MOONSHOT = \"moonshot\"\n OPENROUTER = \"openrouter\"\n OPENAI = \"openai\"\n GROQ = \"groq\"\n TOGETHER = \"together\"\n DEEPSEEK = \"deepseek\"\n\n\nclass IndexMode(str, Enum):\n \"\"\"Indexing modes for conversation chunks\"\"\"\n DENSE = \"dense\" # Dense embedding only\n SPARSE = \"sparse\" # Sparse embedding only (BM25)\n HYBRID = \"hybrid\" # Both dense and sparse\n\n\nclass ChunkingStrategy(str, Enum):\n \"\"\"Strategies for chunking conversations\"\"\"\n FIXED_ROUNDS = \"fixed_rounds\" # Fixed number of rounds per chunk\n SEMANTIC = \"semantic\" # Semantic boundaries\n TIME_BASED = \"time_based\" # Based on timestamp gaps\n\n\n@dataclass\nclass LLMConfig:\n \"\"\"LLM configuration\"\"\"\n provider: str = \"kimi\" # Default provider\n model: Optional[str] = None # Will use provider defaults if not specified\n api_key: Optional[str] = None # Will read from env if not provided\n temperature: float = 0.7\n max_tokens: int = 2048\n stream: bool = True\n \n # Provider-specific defaults\n PROVIDER_DEFAULTS = {\n \"siliconflow\": {\n \"model\": \"Qwen/Qwen3-235B-A22B-Thinking-2507\",\n \"base_url\": \"https://api.siliconflow.cn/v1\"\n },\n \"doubao\": {\n \"model\": \"doubao-seed-1-6-thinking-250715\",\n \"base_url\": \"https://ark.cn-beijing.volces.com/api/v3\"\n },\n \"kimi\": {\n \"model\": \"kimi-k2-0905-preview\",\n \"base_url\": \"https://api.moonshot.cn/v1\"\n },\n \"moonshot\": {\n \"model\": \"kimi-k2-0905-preview\",\n \"base_url\": \"https://api.moonshot.cn/v1\"\n },\n \"openrouter\": {\n \"model\": \"openai/gpt-4o-2024-11-20\",\n \"base_url\": \"https://openrouter.ai/api/v1\"\n },\n \"openai\": {\n \"model\": \"gpt-4o-2024-11-20\",\n \"base_url\": \"https://api.openai.com/v1\"\n },\n \"groq\": {\n \"model\": \"llama-3.3-70b-versatile\",\n \"base_url\": \"https://api.groq.com/openai/v1\"\n },\n \"together\": {\n \"model\": \"meta-llama/Llama-3.3-70B-Instruct-Turbo\",\n \"base_url\": \"https://api.together.xyz\"\n },\n \"deepseek\": {\n \"model\": \"deepseek-reasoner\",\n \"base_url\": \"https://api.deepseek.com/v1\"\n }\n }\n \n def get_client_config(self) -> tuple[Dict[str, Any], str]:\n \"\"\"Get OpenAI client configuration\"\"\"\n provider = self.provider.lower()\n defaults = self.PROVIDER_DEFAULTS.get(provider, {})\n \n # Determine API key\n api_key = self.api_key or os.getenv(f\"{provider.upper()}_API_KEY\")\n if not api_key and provider == \"moonshot\":\n api_key = os.getenv(\"KIMI_API_KEY\") # Fallback for moonshot\n \n # Determine model\n model = self.model or defaults.get(\"model\", \"gpt-4o\")\n \n # Build client config\n client_config = {\"api_key\": api_key}\n \n # Add base URL if needed\n if base_url := defaults.get(\"base_url\"):\n client_config[\"base_url\"] = base_url\n \n return client_config, model\n\n\n@dataclass\nclass ChunkingConfig:\n \"\"\"Configuration for conversation chunking\"\"\"\n strategy: ChunkingStrategy = ChunkingStrategy.FIXED_ROUNDS\n rounds_per_chunk: int = 20 # Number of rounds per chunk for FIXED_ROUNDS\n overlap_rounds: int = 2 # Number of overlapping rounds between chunks\n include_metadata: bool = True # Include conversation metadata in chunks\n min_chunk_size: int = 5 # Minimum number of rounds in a chunk\n max_chunk_size: int = 50 # Maximum number of rounds in a chunk\n\n\n@dataclass\nclass IndexConfig:\n \"\"\"Configuration for RAG indexing\"\"\"\n mode: IndexMode = IndexMode.HYBRID\n embedding_model: str = \"text-embedding-3-small\" # OpenAI embedding model\n embedding_dim: int = 1536 # Dimension of embeddings\n index_path: str = \"indexes/memory_index\"\n chunk_store_path: str = \"data/chunk_store.json\"\n enable_contextual: bool = True # Add contextual information to chunks\n contextual_window: int = 2 # Number of surrounding rounds for context\n\n\n@dataclass\nclass EvaluationConfig:\n \"\"\"Configuration for evaluation framework\"\"\"\n test_cases_dir: str = \"../../week2/user-memory-evaluation/test_cases\"\n results_dir: str = \"results\"\n enable_verbose: bool = True\n save_trajectories: bool = True\n max_iterations: int = 10 # Max iterations for ReAct pattern\n enable_caching: bool = True # Cache indexed conversations\n use_llm_judge: bool = False # Use LLM to evaluate answers\n\n\n@dataclass\nclass AgentConfig:\n \"\"\"Agent behavior configuration\"\"\"\n enable_reasoning: bool = True # Show reasoning steps\n enable_citations: bool = True # Include citations in responses\n max_search_results: int = 5 # Maximum search results to consider\n confidence_threshold: float = 0.7 # Minimum confidence for answers\n enable_multi_search: bool = True # Allow multiple searches per query\n max_searches_per_query: int = 3 # Maximum searches allowed\n verbose: bool = True # Enable verbose output\n\n\n@dataclass\nclass Config:\n \"\"\"Main configuration container\"\"\"\n llm: LLMConfig = field(default_factory=LLMConfig)\n chunking: ChunkingConfig = field(default_factory=ChunkingConfig)\n index: IndexConfig = field(default_factory=IndexConfig)\n evaluation: EvaluationConfig = field(default_factory=EvaluationConfig)\n agent: AgentConfig = field(default_factory=AgentConfig)\n \n @classmethod\n def from_env(cls) -> \"Config\":\n \"\"\"Create configuration from environment variables\"\"\"\n config = cls()\n \n # Override with environment variables\n if provider := os.getenv(\"LLM_PROVIDER\"):\n config.llm.provider = provider\n \n if model := os.getenv(\"LLM_MODEL\"):\n config.llm.model = model\n \n if rounds := os.getenv(\"ROUNDS_PER_CHUNK\"):\n config.chunking.rounds_per_chunk = int(rounds)\n \n if index_mode := os.getenv(\"INDEX_MODE\"):\n config.index.mode = IndexMode(index_mode)\n \n return config\n \n def save(self, path: str):\n \"\"\"Save configuration to JSON file\"\"\"\n import json\n \n config_dict = {\n \"llm\": {\n \"provider\": self.llm.provider,\n \"model\": self.llm.model,\n \"temperature\": self.llm.temperature,\n \"max_tokens\": self.llm.max_tokens,\n \"stream\": self.llm.stream\n },\n \"chunking\": {\n \"strategy\": self.chunking.strategy,\n \"rounds_per_chunk\": self.chunking.rounds_per_chunk,\n \"overlap_rounds\": self.chunking.overlap_rounds,\n \"include_metadata\": self.chunking.include_metadata\n },\n \"index\": {\n \"mode\": self.index.mode,\n \"embedding_model\": self.index.embedding_model,\n \"enable_contextual\": self.index.enable_contextual,\n \"contextual_window\": self.index.contextual_window\n },\n \"evaluation\": {\n \"enable_verbose\": self.evaluation.enable_verbose,\n \"save_trajectories\": self.evaluation.save_trajectories,\n \"max_iterations\": self.evaluation.max_iterations\n },\n \"agent\": {\n \"enable_reasoning\": self.agent.enable_reasoning,\n \"enable_citations\": self.agent.enable_citations,\n \"max_search_results\": self.agent.max_search_results,\n \"confidence_threshold\": self.agent.confidence_threshold\n }\n }\n \n with open(path, 'w') as f:\n json.dump(config_dict, f, indent=2)\n \n @classmethod\n def load(cls, path: str) -> \"Config\":\n \"\"\"Load configuration from JSON file\"\"\"\n import json\n \n with open(path, 'r') as f:\n config_dict = json.load(f)\n \n config = cls()\n \n # Update LLM config\n if \"llm\" in config_dict:\n for key, value in config_dict[\"llm\"].items():\n setattr(config.llm, key, value)\n \n # Update other configs similarly\n for section in [\"chunking\", \"index\", \"evaluation\", \"agent\"]:\n if section in config_dict:\n section_config = getattr(config, section)\n for key, value in config_dict[section].items():\n # Handle enums\n if key == \"strategy\" and section == \"chunking\":\n value = ChunkingStrategy(value)\n elif key == \"mode\" and section == \"index\":\n value = IndexMode(value)\n setattr(section_config, key, value)\n \n return config\n"}}}} |