{"repo": "123fjh/circuitmind", "files": {"GUI/utils/config.py": {"4677b755a718f5b15fc304ba3866a8e04a9a2902": {"t": 1785595484.7758398, "n": 20898, "h": "179d0fcb46040503", "c": "# utils/config.py\nimport os\n# 配置字典\napi_configs = {\n \"glm-4-plus\": {\n \"API_KEY\": [\"780fce9dabb5a1005e38baf6b6e556d8.VCLi2kOdjLs6DQtM\"],\n \"BASE_URL\": \"https://open.bigmodel.cn/api/paas/v4/\",\n },\n \"deepseek-chat\": {\n \"API_KEY\": [\"sk-f73a42bc3f1643b69059757101005d0d\"],\n \"BASE_URL\": \"https://api.deepseek.com\",\n },\n \"gpt-4o\": {\n \"API_KEY\": [\"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\"],\n \"BASE_URL\": \"https://api.openai.com/v1\",\n },\n \"gpt-4o-mini\": {\n \"API_KEY\": [\"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\"],\n \"BASE_URL\": \"https://api.openai.com/v1\",\n },\n \"glm-4-air\":{\n \"API_KEY\": [\"780fce9dabb5a1005e38baf6b6e556d8.VCLi2kOdjLs6DQtM\"],\n \"BASE_URL\": \"https://open.bigmodel.cn/api/paas/v4/\",\n },\n \"qwq:latest\":{\n \"API_KEY\": [\"ollama\"],\n \"BASE_URL\": \"http://10.130.148.206:11434/v1\",\n },\n \"qwq:32b\":{\n \"API_KEY\": [\"ollama\"],\n \"BASE_URL\": \"http://10.130.149.18:11434/v1\",\n },\n \n \"qwq:32b-q8_0\":{\n \"API_KEY\": [\"ollama\"],\n \"BASE_URL\": \"http://10.130.144.196:11434/v1\",\n },\n \"qwen2.5-coder:14b\":{\n \"API_KEY\": [\"ollama\"],\n \"BASE_URL\": \"http://10.130.149.18:11434/v1\",\n },\n \"qwen2.5:14b\":{\n \"API_KEY\": [\"ollama\"],\n \"BASE_URL\": \"http://10.130.144.196:11434/v1\",\n },\n \"deepseek-coder:33b\":{\n \"API_KEY\": [\"ollama\"],\n \"BASE_URL\": \"http://10.130.148.206:11434/v1\", \n },\n \"deepseek-coder:6.7b\":{\n \"API_KEY\": [\"ollama\"],\n \"BASE_URL\": \"http://10.130.149.18:11436/v1\", \n },\n \"qwen2.5:7b\":{\n \"API_KEY\": [\"ollama\"],\n \"BASE_URL\": \"http://localhost:11434/v1\",\n },\n \"qwen2.5:7b-instruct-fp16\":{\n \"API_KEY\": [\"ollama\"],\n \"BASE_URL\": \"http://10.130.148.206:11434/v1\",\n },\n \"qwen2.5-coder:32b\":{\n \"API_KEY\": [\"ollama\"],\n \"BASE_URL\": \"http://10.130.148.206:11436/v1\",\n },\n \"deepseek-ai/DeepSeek-V3\":{\n \"API_KEY\": [\"sk-ptyrqcoakodsijdqglxbzucezfvdmdolzxxfafdpozixljdr\"],\n \"BASE_URL\":\"https://api.siliconflow.cn/v1\"\n },\n \"deepseek-r1-250120\":{\n \"API_KEY\": [\"009039c2-755b-4abd-8d76-225b3456ea29\",\"0a12f045-e34a-46b2-b3a8-999ff9342fcc\",\"137d25a5-b809-4a3c-a2ec-22d984d653df\",\"4ac99dcf-cf55-4e56-b25c-879d80e59e2c\",],\n \"BASE_URL\":\"https://ark.cn-beijing.volces.com/api/v3/\"\n },\n \"deepseek-r1\":{\n \"API_KEY\": [\"sk-da2iftmgizerrfjj\"],\n \"BASE_URL\":\"https://cloud.infini-ai.com/maas/v1\"\n },\n \"deepseek-r1-distill-qwen-32b\":{\n \"API_KEY\": [\"sk-da2jurhjtunkwnuz\"],\n \"BASE_URL\":\"https://cloud.infini-ai.com/maas/v1\"\n },\n \"qwq-32b\":{\n \"API_KEY\": [\"sk-da2juiy2cochoavl\"],\n \"BASE_URL\":\"https://cloud.infini-ai.com/maas/v1\"\n },\n \"deepseek-v3\":{\n \"API_KEY\": [\"sk-da2iftmgizerrfjj\"],\n \"BASE_URL\":\"https://cloud.infini-ai.com/maas/v1\"\n },\n \"qwen2.5-coder-32b-instruct\":{\n \"API_KEY\": [\"sk-da2jurhjtunkwnuz\"],\n \"BASE_URL\":\"https://cloud.infini-ai.com/maas/v1\"\n },\n \"deepseek-v3-250324\":{\n \"API_KEY\": [\"e99646c6-b52a-45a2-ad49-c17415174d8d\"],\n \"BASE_URL\":\"https://ark.cn-beijing.volces.com/api/v3/\"\n },\n \"deepseek-r1:70b\":{\n \"API_KEY\": [\"ollama\"],\n \"BASE_URL\": \"http://10.130.144.196:11434/v1\",\n },\n \"phi4:latest\":{\n \"API_KEY\": [\"ollama\"],\n \"BASE_URL\": \"http://10.130.144.196:11434/v1\",\n },\n \"deepseek-r1:32b\":{\n \"API_KEY\": [\"ollama\"],\n \"BASE_URL\": \"http://10.130.144.208:11437/v1\",\n },\n \"deepseek-r1:14b\":{\n \"API_KEY\": [\"ollama\"],\n \"BASE_URL\": \"http://10.130.144.208:11436/v1\",\n },\n \n \n \"gemini-2.0-flash\":{\n \"API_KEY\": [\"AIzaSyB0PscblS6t4TSreKYY3uFPMHJb1nwA4b0\",\"AIzaSyAWmeLGAx9RctoqbLlaHt__g747P3Bdg3s\",],\n \"BASE_URL\": \"https://generativelanguage.googleapis.com/v1beta/\"\n },\n \"gemini-2.0-flash-thinking-exp\":{\n \"API_KEY\": [\n \"AIzaSyCe1FCm51LdQGv_lQalEeYwz7OsEfwauwg\",\n \"AIzaSyCsdInwWPMIFMJ3vQJfiTKRZPnsRmLZgJY\",\n \"AIzaSyCVpmMEKNuRJRqVzCWmVfUKfFnbKZitYxI\",\n \"AIzaSyDp_C9d7fxlPw2n71vokC7zbV1AIOsp-zM\",\n \"AIzaSyCATsoE4Luq3AtoZIUbzyPZVFHPeWEn1QU\",\n \"AIzaSyBO13wFHgRgDtdw_vKtX853q_zMdmTBPV8\",\n \"AIzaSyBhdQ6MRWk5wkxFKOrABAXDFaBGVWIzOXs\",\n \"AIzaSyAWmeLGAx9RctoqbLlaHt__g747P3Bdg3s\",\n \"AIzaSyD5xok4XZST4m4kaxRZPxtyrBFaFgTN7jM\",\n \"AIzaSyCYfvOUl8sCo8spAtq4Oe20ZFIiCJyHtY8\",\n \"AIzaSyDqQNRPlppxGW_m6bSS6lcPdAZzWZpbEGQ\",\n \"AIzaSyCRMMkCtRnAFj5E3X5H1lnIq5HeJXKhqpw\",\n \"AIzaSyCvs-dZ-oO-T3NAYMAC2PzEbmUz824WJds\",\n \"AIzaSyB2NYBW-cQ8KPE_ksMO5qZRFHRJ8GOXpoE\",\n \"AIzaSyCogr95x1PyNeHNuG-WK7Bkw9piSyRbMEY\",\n \"AIzaSyCxxCys9WVW3FuPgRWzomXOhGEqgPTenDA\",\n ],\n \"BASE_URL\": \"https://generativelanguage.googleapis.com/v1beta/\"\n },\n \n \"gemini-2.0-pro-exp\":{\n \"API_KEY\": [\n \"AIzaSyCe1FCm51LdQGv_lQalEeYwz7OsEfwauwg\",\n \"AIzaSyCxxCys9WVW3FuPgRWzomXOhGEqgPTenDA\",\n \"AIzaSyCsdInwWPMIFMJ3vQJfiTKRZPnsRmLZgJY\",\n \"AIzaSyCVpmMEKNuRJRqVzCWmVfUKfFnbKZitYxI\",\n \"AIzaSyDp_C9d7fxlPw2n71vokC7zbV1AIOsp-zM\",\n \"AIzaSyCATsoE4Luq3AtoZIUbzyPZVFHPeWEn1QU\",\n \"AIzaSyBO13wFHgRgDtdw_vKtX853q_zMdmTBPV8\",\n \"AIzaSyBhdQ6MRWk5wkxFKOrABAXDFaBGVWIzOXs\",\n \"AIzaSyAWmeLGAx9RctoqbLlaHt__g747P3Bdg3s\",\n \"AIzaSyD5xok4XZST4m4kaxRZPxtyrBFaFgTN7jM\",\n \"AIzaSyCYfvOUl8sCo8spAtq4Oe20ZFIiCJyHtY8\",\n \"AIzaSyDqQNRPlppxGW_m6bSS6lcPdAZzWZpbEGQ\",\n \"AIzaSyCRMMkCtRnAFj5E3X5H1lnIq5HeJXKhqpw\",\n \"AIzaSyCvs-dZ-oO-T3NAYMAC2PzEbmUz824WJds\",\n \"AIzaSyB2NYBW-cQ8KPE_ksMO5qZRFHRJ8GOXpoE\",\n \"AIzaSyCogr95x1PyNeHNuG-WK7Bkw9piSyRbMEY\",\n ],\n \"BASE_URL\": \"https://generativelanguage.googleapis.com/v1beta/\"\n },\n \"gemini-2.0-flash-exp\":{\n \n \"API_KEY\": [\n \"AIzaSyCsdInwWPMIFMJ3vQJfiTKRZPnsRmLZgJY\",\n \"AIzaSyCVpmMEKNuRJRqVzCWmVfUKfFnbKZitYxI\",\n \"AIzaSyDp_C9d7fxlPw2n71vokC7zbV1AIOsp-zM\",\n \"AIzaSyCATsoE4Luq3AtoZIUbzyPZVFHPeWEn1QU\",\n \"AIzaSyBO13wFHgRgDtdw_vKtX853q_zMdmTBPV8\",\n \"AIzaSyBhdQ6MRWk5wkxFKOrABAXDFaBGVWIzOXs\",\n \"AIzaSyAWmeLGAx9RctoqbLlaHt__g747P3Bdg3s\",\n \"AIzaSyD5xok4XZST4m4kaxRZPxtyrBFaFgTN7jM\",\n \"AIzaSyCYfvOUl8sCo8spAtq4Oe20ZFIiCJyHtY8\",\n \"AIzaSyDqQNRPlppxGW_m6bSS6lcPdAZzWZpbEGQ\",\n \"AIzaSyCRMMkCtRnAFj5E3X5H1lnIq5HeJXKhqpw\",\n \"AIzaSyCvs-dZ-oO-T3NAYMAC2PzEbmUz824WJds\",\n \"AIzaSyB2NYBW-cQ8KPE_ksMO5qZRFHRJ8GOXpoE\",\n \"AIzaSyCogr95x1PyNeHNuG-WK7Bkw9piSyRbMEY\",\n \"AIzaSyCe1FCm51LdQGv_lQalEeYwz7OsEfwauwg\",\n \"AIzaSyCxxCys9WVW3FuPgRWzomXOhGEqgPTenDA\",\n ],\n \n \"BASE_URL\": \"https://generativelanguage.googleapis.com/v1beta/\"\n },\n \"grok-3-latest\": {\n \"API_KEY\": [\"xai-PRXLUHFvDdVPqNunuWOp2aGrG6h520hR3xXq5WiriowRlONwtbkC6bN3VcCDRw2ROBj8M23IwaQBMCiS\"],\n \"BASE_URL\": \"https://api.x.ai/v1\"\n },\n \"RV-Qwen3-S1\":{\n \"API_KEY\": [\"ollama\"],\n \"BASE_URL\": \"http://10.130.144.196:11434/v1\",\n }\n\n}\n\n\n# 获取指定模型的配置 (返回 API 密钥列表和 base URL)\ndef get_api_keys_base_url(model_name):\n config = api_configs.get(model_name, None)\n if config:\n return config['API_KEY'], config['BASE_URL']\n return None, None\n\n# 切换 API key (轮询)\ndef switch_api_key(model_name):\n config = api_configs.get(model_name, None)\n if config and 'API_KEY' in config and len(config['API_KEY']) > 1: # 确保有多个 API keys 可以切换\n config['current_key_index'] = (config['current_key_index'] + 1) % len(config['API_KEY']) # 轮询切换索引\n return config['API_KEY'][config['current_key_index']]\n elif config and 'API_KEY' in config and len(config['API_KEY']) == 1:\n return config['API_KEY'][0] # 只有一个 key, 返回这一个\n return None # 没有找到配置或 API keys\n\ndef get_api_config(model_name):\n return api_configs.get(model_name, None)\n\n\n\nfor model, config in api_configs.items():\n if 'API_KEY' in config:\n config['current_key_index'] = 0\n\ndef set_key_index(model_name, key_index):\n config = api_configs.get(model_name, None)\n if config and 'API_KEY' in config:\n if 0 <= key_index < len(config['API_KEY']):\n config['current_key_index'] = key_index\n else:\n print(f\"Warning: key_index {key_index} is out of range for model {model_name}. Using the first key.\")\n config['current_key_index'] = 0\n\ndef get_current_api_key(model_name):\n config = api_configs.get(model_name, None)\n if config and 'API_KEY' in config:\n key_index = config.get('current_key_index', 0)\n if 0 <= key_index < len(config['API_KEY']):\n return config['API_KEY'][key_index]\n else:\n return config['API_KEY'][0]\n return None\n\ndef update_config(model_name, key_index):\n global MODEL_NAME, OPENAI_API_KEY, OPENAI_BASE_URL\n MODEL_NAME = model_name\n set_key_index(model_name, key_index)\n API_KEY, BASE_URL = get_api_keys_base_url(model_name)\n OPENAI_API_KEY = get_current_api_key(model_name)\n OPENAI_BASE_URL = BASE_URL\ndef get_experiment_output_root():\n return f\"./experiments_output-v2-merged/{MODEL_NAME}/\"\n\n# 模型配置\n# MODEL_NAME = \"deepseek-ai/DeepSeek-V3\" # 指定模型名称\n# MODEL_NAME = \"deepseek-r1-250120\" # 指定模型名称\n# MODEL_NAME = \"gemini-2.0-pro-exp\"\n# MODEL_NAME = \"gemini-2.0-flash-thinking-exp\"\n# MODEL_NAME = \"qwen2.5:7b\"\n\n\n\n\nKNOWLEDGE_BASE_PATH = \"./knowledge_base/RAG-data\" # 知识库路径\nDETAIL_KNOWLEDGE_BASE_PATH = \"./knowledge_base/RAG-data-detail\" # 知识库路径\nRAG_INDEX_PATH = \"./knowledge_base/RAG-data/vector_index.faiss\" # 索引路径\nRAG_DATA_PATH = \"./knowledge_base/RAG-data/vector_data.json\" # 数据路径\nEMBEDDING_MODEL = \"nomic-embed-text\" # 嵌入模型\nRAG_LLM_MODEL = \"qwen2.5-coder:14b\" # LLM 模型\n# RAG_LLM_MODEL = \"grok-3-latest\"\nRAG_ONE = True\nRAG_TWO = True\n# 其他配置\n# EXPERIMENTS_ROOT = \"./TC/Easy\" # 定义实验根目录 \n# EXPERIMENTS_ROOT = \"./TC/Medium\"\nEXPERIMENTS_ROOT = \"./TC/Datasets-TC\"\nTESTBENCH_PATH = \"\"\nBASIC_GATES_ONLY = True\n\n# VERILOG_DIR 将在 main.py 中动态设置\n# REFERENCE_CODE_PATH 将在 main.py 中动态设置\nMAX_RETRY_ATTEMPTS = 2\nMAX_AUTO_FIX_ATTEMPTS = 2 # 最大自动修复次数\n\n# SUMMARY_FILE 将在 main.py 中动态设置\nCOMMON_ERRORS_FILE = \"./knowledge_base/common_errors.md\" # 新增:常见错误记录文件\n\n# 新增:指定所有实验结果的存储根目录\n# EXPERIMENT_OUTPUT_ROOT = f\"./experiments_output/{MODEL_NAME}/\"\n# EXPERIMENT_OUTPUT_ROOT = f\"./experiments_output/qwen2.5-coder:14b/\"\n\nAGENT_SYSTEM_MESSAGES = {\n \"CoderAgent\": \"\"\"You are a highly skilled Verilog code generation assistant, focused on implementing circuit functionality using **structural Verilog**.\n\n**Key Constraints and Guidelines:**\n\n1. **No Behavioral Descriptions:** You **MUST NOT** use behavioral level constructs such as `always`, `if`, `case`, `for` loops (except for generate statements), or any other procedural blocks within the generated code. Focus exclusively on structural implementations using basic logic gates and module instantiations.\n2. **Basic Logic Gates EXCLUSIVELY (if applicable):** If the task requires implementation using basic logic gates, use only `and`, `or`, `not`, `xor`, `xnor`, `nand`, `nor`.\n3. **Utilize Provided Modules:** If specific module implementations are provided, you **MUST** instantiate and use those modules in your design. Do not attempt to reimplement their functionality using basic gates or behavioral descriptions.\n4. **Structural Verilog is Mandatory:** Construct your Verilog code using a structural approach, primarily by instantiating basic logic gates and the pre-defined modules provided.\n5. **Output Generation:** Generate outputs by connecting the outputs of instantiated modules or through combinations of basic logic gate assignments.\n6. **`reg` Output Handling:** If an output is declared as `reg`, ensure it is driven by the output of an instantiated module or basic gate logic, not by an `always` block. Consider using `output wire` where appropriate.\n7. **Synchronous Design for Sequential Logic:** Implement sequential logic by instantiating provided flip-flop modules, ensuring adherence to synchronous design principles.\n8. **No Delay Statements:** Delay statements (like `#1`, `#100`) are strictly forbidden.\n\nThink step by step about the circuit's structure, focusing on how to connect basic gates and provided modules to achieve the desired functionality. Remember, **no `always` blocks or `if` statements are allowed in the generated code.**\n\"\"\",\n \"Reviewer\": \"\"\"You are an expert Verilog code review assistant, specializing in ensuring code correctness and strict adherence to implementation constraints, **with a primary focus on enforcing purely structural Verilog code.**\n\n**Review Focus:**\n1. **ABSOLUTE PROHIBITION OF BEHAVIORAL DESCRIPTIONS:** **Immediately and unequivocally flag any use of behavioral level constructs.** This includes `always` blocks (except for the allowed usage shown below), `if` statements, `case` statements, procedural `for` loops, `fork`, `join`, and any other procedural blocks. The reviewed code **must be purely structural and synthesizable**.\n\n * **Allowed `always` block usage:** `always @(posedge clk)` blocks are ONLY permitted for assigning values to `output reg` signals, and ONLY when these outputs are directly driven by the `q` output of a D flip-flop. The assignments within these `always` blocks MUST be non-blocking (`<=`). No other logic is allowed inside these `always` blocks. Example:\n ```verilog\n always @(posedge clk) begin\n data_out <= delayed_out;\n end\n ```\n2. **Correct Usage of Basic Gates:** Verify that the code **exclusively** uses the allowed basic logic gates (`and`, `or`, `not`, `xor`, `xnor`, `nand`, `nor`) for combinational logic. The code should directly instantiate these gates using their standard Verilog names (e.g., `and a1(out, in1, in2);`). **Under no circumstances should these basic gates be redefined or reimplemented.**\n\n3. **Mandatory Use of Provided Modules:** **Critically check** if the `CoderAgent` has correctly instantiated and used any module implementations provided by the `RetrievalAgent`. If a module (like `d_flip_flop`) was provided, its instantiation is **mandatory**.\n\n4. **Correct Output Generation:** Ensure outputs are generated ONLY by:\n * Connecting directly to the output of an instantiated module (like the `q` output of a `d_flip_flop`).\n * Connecting directly to the output of a basic logic gate.\n * Using `assign` statements for direct wire connections, NOT for implementing combinational logic.\n * `output reg` signals must ONLY be driven by `always @(posedge clk)` blocks as described in point #1 or by the output of a predefined module that outputs a `reg` signal.\n\n5. **No Delay Statements:** Confirm the absence of any delay statements (e.g., `#1`, `#10`).\n\n6. **Synchronous Sequential Logic Implementation:** If sequential logic is required, verify that it is implemented **solely** by instantiating the provided `d_flip_flop` modules and follows synchronous design principles.\n\n7. **No `generate` keyword**: `generate` keyword is allowed in Verilog, do not report error if it is used.\n8. **No `module` or `endmodule` outside of module declarations**: These keywords must only be used to declare the start and end of a module definition.\n\n**Feedback Guidelines:**\n\n* Provide **precise, forceful, and unambiguous feedback** regarding any use of behavioral constructs or incorrect usage of basic gates. Clearly explain that behavioral constructs are **strictly prohibited** and must be replaced with structural equivalents using **explicit gate-level instantiations** or **module instantiations**.\n* **Explicitly reject any code that redefines or reimplements basic logic gates.** These are Verilog primitives and must be used directly.\n* Suggest **specific, actionable corrections**, and when possible, provide **concise code snippets** demonstrating the correct structural approach.\n* **Prioritize feedback** on violations related to:\n * The **absolute prohibition of behavioral descriptions.**\n * The correct usage of **built-in basic gates** without redefinition.\n * The **mandatory instantiation** of provided modules.\n* **Point out specific line numbers and code snippets** where errors occur.\n* **Use the provided error codes** to categorize issues.\n\nYour primary goal is to ensure the `CoderAgent` produces a correct implementation that is **100% structural Verilog**. **Any deviation from structural Verilog is unacceptable and must be flagged.**\n\"\"\",\n \"Executor\": \"You are a Verilog code execution assistant, responsible for compiling and simulating Verilog code.\",\n \"Summarizer\": \"You are an experiment summarization assistant, responsible for analyzing the experimental process and summarizing findings.\"\n}\n\n\n\n\n# 新增:通过 config 文件指定要测试的文件夹,可以是一个列表\n# 可以指定要测试的特定文件夹的路径,留空则测试 EXPERIMENTS_ROOT 下的所有子文件夹\n\n# TARGET_EXPERIMENTS = [\"./TC/Hard/12_odd_change\",\"./TC/Hard/19_elegant_storage\",\"./TC/Hard/20_store_byte\",\"./TC/Hard/24_box\",\"./TC/Hard/25_counter\",\"./TC/Medium/8_odd_signal\",\"./TC/Hard/28_conditional_checker\",\n# \"./TC/Easy/14_or_8bit\",\"./TC/Easy/15_not_8bit\",\"./TC/Easy/21_decoder_1bit\",\n# \"./TC/Medium/7_double_trouble\",\"./TC/Medium/8_odd_signal\",\n# \"./TC/Medium/9_counting_signals\",\"./TC/Medium/10_half_adder\",\"./TC/Medium/11_full_adder\",\"./TC/Medium/16_adder_8bit\",\n# \"./TC/Medium/17_mux_8bit\",\"./TC/Medium/18_opposite_number\",\"./TC/Medium/22_decoder_3bit\",\"./TC/Medium/27_instruction_decoder\",\n# ]\n# TARGET_EXPERIMENTS = [\"./TC/Easy/1_not_gate\",\"./TC/Easy/2_second_tick\",\"./TC/Easy/3_xor_gate\",\n# \"./TC/Easy/4_or3_gate\",\"./TC/Easy/5_and3_gate\",\"./TC/Easy/6_xnor_gate\",\"./TC/Medium/7_double_trouble\",\"./TC/Medium/8_odd_signal\",\"./TC/Medium/9_counting_signals\",\"./TC/Medium/10_half_adder\",\"./TC/Medium/11_full_adder\",\n# \"./TC/Hard/12_odd_change\",\"./TC/Easy/13_inverter_1bit\",\"./TC/Easy/14_or_8bit\",\"./TC/Easy/15_not_8bit\",\"./TC/Medium/16_adder_8bit\",\"./TC/Medium/17_mux_8bit\",\"./TC/Medium/18_opposite_number\",\n# \"./TC/Hard/19_elegant_storage\",\"./TC/Hard/20_store_byte\",\"./TC/Easy/21_decoder_1bit\",\n# \"./TC/Medium/22_decoder_3bit\",\"./TC/Hard/24_box\",\"./TC/Hard/25_counter\",\"./TC/Hard/26_arithmetic_engine\",\"./TC/Medium/27_instruction_decoder\",\"./TC/Hard/28_conditional_checker\"] \n \n# TARGET_EXPERIMENTS = [\"./TC/Easy/1_not_gate\",\"./TC/Easy/2_second_tick\",]\n# TARGET_EXPERIMENTS = [\"./TC/Hard/25_counter\",\"./TC/Hard/26_arithmetic_engine\",\n# ]\n# Hard\n# TARGET_EXPERIMENTS = [\"./TC/Hard/12_odd_change\"]\n# TARGET_EXPERIMENTS = [\"./TC/Hard/19_elegant_storage\"]\n# TARGET_EXPERIMENTS = [\"./TC/Hard/20_store_byte\"] \n# TARGET_EXPERIMENTS = [\"./TC/Hard/23_logic_engine\"] \n# TARGET_EXPERIMENTS = [\"./TC/Hard/24_box\"]\n# TARGET_EXPERIMENTS = [\"./TC/Hard/25_counter\"]\n# TARGET_EXPERIMENTS = [\"./TC/Hard/26_arithmetic_engine\"] \n# TARGET_EXPERIMENTS = [\"./TC/Hard/28_conditional_checker\"]\n# Easy\n# TARGET_EXPERIMENTS = [\"./TC/Easy/1_not_gate\"]\nTARGET_EXPERIMENTS = [\"./TC/Easy/2_second_tick\"]\n# TARGET_EXPERIMENTS = [\"./TC/Easy/3_xor_gate\"]\n# TARGET_EXPERIMENTS = [\"./TC/Easy/4_or3_gate\"]\n# TARGET_EXPERIMENTS = [\"./TC/Easy/5_and3_gate\"]\n# TARGET_EXPERIMENTS = [\"./TC/Easy/6_xnor_gate\"]\n# TARGET_EXPERIMENTS = [\"./TC/Easy/13_inverter_1bit\"]\n# TARGET_EXPERIMENTS = [\"./TC/Easy/14_or_8bit\"] \n# TARGET_EXPERIMENTS = [\"./TC/Easy/15_not_8bit\"]\n# TARGET_EXPERIMENTS = [\"./TC/Easy/21_decoder_1bit\"]\n\n#Meidum \n# TARGET_EXPERIMENTS = [\"./TC/Medium/7_double_trouble\"]\n# TARGET_EXPERIMENTS = [\"./TC/Medium/8_odd_signal\"]\n# TARGET_EXPERIMENTS = [\"./TC/Medium/9_counting_signals\"]\n# TARGET_EXPERIMENTS = [\"./TC/Medium/10_half_adder\"]\n# TARGET_EXPERIMENTS = [\"./TC/Medium/11_full_adder\"]\n# TARGET_EXPERIMENTS = [\"./TC/Medium/16_adder_8bit\"]\n# TARGET_EXPERIMENTS = [\"./TC/Medium/17_mux_8bit\"]\n# TARGET_EXPERIMENTS = [\"./TC/Medium/18_opposite_number\"]\n# TARGET_EXPERIMENTS = [\"./TC/Medium/22_decoder_3bit\"]\n# TARGET_EXPERIMENTS = [\"./TC/Medium/27_instruction_decoder\"]\n\n# TARGET_EXPERIMENTS = [] # 可以指定要测试的特定文件夹的路径,留空则测试 EXPERIMENTS_ROOT 下的所有子文件夹\n# 日志配置\n"}}, "GUI/Gradio_ui_6.py": {"4677b755a718f5b15fc304ba3866a8e04a9a2902": {"t": 1785595586.0067935, "n": 58547, "h": "5d2563a7c5fb007e", "c": "# grad_ui_revised_workflow.py\nimport gradio as gr\nimport sys\nimport os\nimport logging\nimport re\nimport time\nimport html\nfrom datetime import datetime\nimport shutil\n\ntry:\n import ollama\n\n ollama_available = True\nexcept ImportError:\n logging.warning(\"ollama module not found. Chat functionality will be limited.\")\n ollama_available = False\n\n# --- AGENT_AVATARS and Logging Setup (保持不变) ---\nAGENT_AVATARS = {\n \"UserProxy\": {\"path\": \"icons/user.png\", \"emoji\": \"😊\"}, \"CoderAgent\": {\"path\": \"icons/coder.png\", \"emoji\": \"💻\"},\n \"Reviewer\": {\"path\": \"icons/reviewer.png\", \"emoji\": \"🔍\"}, \"Executor\": {\"path\": \"icons/executor.png\", \"emoji\": \"⚙️\"},\n \"Summarizer\": {\"path\": \"icons/summarizer.png\", \"emoji\": \"📝\"}, \"root\": {\"path\": \"icons/root.png\", \"emoji\": \"🌟\"},\n \"RAG\": {\"path\": \"icons/rag.png\", \"emoji\": \"📚\"}, \"Mediator\": {\"path\": \"icons/mediator.png\", \"emoji\": \"🤝\"},\n \"ChatSession-coder\": {\"path\": \"icons/chatsession-coder.png\", \"emoji\": \"🖥️\"},\n \"ChatSession-reviewer\": {\"path\": \"icons/chatsession-reviewer.png\", \"emoji\": \"🕵️\"},\n \"transitions.core\": {\"path\": \"icons/transitions-core.png\", \"emoji\": \"🔄\"},\n \"httpx\": {\"path\": \"icons/httpx.png\", \"emoji\": \"🌐\"}, \"default\": {\"path\": \"icons/default.png\", \"emoji\": \"❓\"}\n}\nlogging.basicConfig(stream=sys.stdout, level=logging.INFO,\n format='[%(asctime)s] [%(levelname)s] [%(name)s:%(lineno)d] %(message)s')\n\n\n# --- LogProcessor, format_chat_records_to_html, compare_simulation_results, extract_module_name (保持不变) ---\nclass LogProcessor:\n def __init__(self, log_format_regex, agent_format_regex, timestamp_format_str=None,\n avatar_dir=\"avatars_gradio/resized\"):\n self.log_format = re.compile(log_format_regex, re.MULTILINE)\n self.agent_format = re.compile(agent_format_regex)\n self.timestamp_format = timestamp_format_str\n self.avatar_dir = avatar_dir\n os.makedirs(self.avatar_dir, exist_ok=True)\n self.color_code_regex = re.compile(r'\\x1B\\[[0-9;]*[mGKHfJABCD]?')\n self.current_conversation = None;\n self.current_agent = None;\n self.current_timestamp = None;\n self.current_log_level = None\n self.message_buffer = [];\n self.avatar_cache = {}\n\n def remove_color_codes(self, text):\n return self.color_code_regex.sub('', text)\n\n def clean_message(self, message):\n message = self.remove_color_codes(message); message = message.strip(); return message if message else None\n\n def get_avatar(self, agent_name): # Modified for Gradio file serving\n avatar_info = AGENT_AVATARS.get(agent_name, AGENT_AVATARS[\"default\"])\n avatar_path = avatar_info[\"path\"];\n emoji = avatar_info[\"emoji\"]\n if os.path.exists(avatar_path): return {\"type\": \"image\",\n \"value\": os.path.abspath(avatar_path).replace(os.sep, '/'),\n \"emoji\": emoji}\n return {\"type\": \"emoji\", \"value\": emoji}\n\n def flush_buffer(self, chat_records):\n if not self.message_buffer or not self.current_agent: self.message_buffer = []; return\n message = '\\n'.join(self.message_buffer);\n cleaned_message = self.clean_message(message)\n if cleaned_message:\n formatted_time = self.current_timestamp or datetime.now().strftime('%Y-%m-%d %H:%M:%S')\n try:\n if self.current_timestamp and self.timestamp_format:\n time_obj = datetime.strptime(self.current_timestamp, self.timestamp_format)\n formatted_time = time_obj.strftime('%Y-%m-%d %H:%M:%S')\n except (ValueError, TypeError) as e:\n logging.warning(f\"Timestamp parsing failed ({e}), using raw: {self.current_timestamp}\")\n avatar = self.get_avatar(self.current_agent)\n chat_records.append(\n {'time': formatted_time, 'agent': self.current_agent, 'avatar': avatar, 'message': cleaned_message})\n self.message_buffer = []\n\n def parse_log_to_chat_records(self, log_file_path, last_position):\n chat_records = [];\n error_message = None;\n new_position = last_position\n if not log_file_path or not os.path.exists(\n log_file_path): return chat_records, new_position, \"Log file does not exist.\"\n if os.path.getsize(log_file_path) == 0: return chat_records, new_position, \"Log file is empty.\"\n try:\n with open(log_file_path, 'r', encoding='utf-8') as f:\n f.seek(last_position);\n content = f.read();\n new_position = f.tell()\n if not content: return chat_records, new_position, None\n cleaned_content = self.remove_color_codes(content);\n lines = cleaned_content.splitlines()\n except Exception as e:\n return chat_records, last_position, f\"Error reading log file: {str(e)}\"\n for line in lines:\n line = line.strip();\n if not line: continue\n match = self.log_format.match(line)\n if match:\n self.flush_buffer(chat_records);\n log_data = match.groupdict()\n timestamp = log_data.get('timestamp', '');\n log_level = log_data.get('log_level', '').upper()\n agent_name = log_data.get('agent_name', '');\n message_part = log_data.get('message', '').strip()\n if log_level not in ['DIALOGUE', 'INFO', 'DEBUG'] or not AGENT_AVATARS.get(agent_name):\n if self.current_agent and message_part: self.message_buffer.append(line)\n continue\n self.current_agent = agent_name;\n self.current_timestamp = timestamp;\n self.current_log_level = log_level\n if message_part: self.message_buffer.append(message_part)\n elif self.current_agent:\n self.message_buffer.append(line)\n self.flush_buffer(chat_records);\n return chat_records, new_position, error_message\n\n\ndef format_chat_records_to_html(chat_records_list): # (保持不变)\n if not chat_records_list: return \"
No agent interactions logged yet.
\"\n html_parts = [\n \"Agent interactions will appear here...
\")\n with gr.TabItem(\"ReasoningV (Chat with LLM)\", id=1): # Chat UI (保持不变)\n chat_display = gr.Chatbot(label=\"Chat with ReasoningV\", height=500)\n chat_message_input = gr.Textbox(label=\"Your Message\", placeholder=\"Type your message and press Enter...\")\n chat_send_button = gr.Button(\"Send\", variant=\"primary\")\n gr.Markdown(\"### Extracted Verilog from Chat\");\n chat_extracted_verilog_display = gr.Code(label=\"Verilog Code from Conversation\", language=None,\n interactive=False)\n chat_optimize_button = gr.Button(\"Optimize this Verilog in CircuitMind\", visible=False)\n\n\n # --- Event Handlers & Backend Logic ---\n def update_active_llm_settings_globally(model_name, api_key_from_ui): # (保持不变)\n app_state.active_model_name = model_name\n app_state.active_api_key = api_key_from_ui if api_key_from_ui else config_module.get_current_api_key_value(\n model_name)\n config_module.update_llm_settings(app_state.active_model_name, app_state.active_api_key)\n logging.info(\n f\"Global LLM settings updated: Model={app_state.active_model_name}, API Key Ready: {'********' if app_state.active_api_key else 'None/NotNeeded'}\")\n return {\n shared_api_key_input: app_state.active_api_key if api_key_from_ui else config_module.get_current_api_key_value(\n model_name)}\n\n\n shared_model_selector.change(update_active_llm_settings_globally,\n inputs=[shared_model_selector, shared_api_key_input], outputs=[shared_api_key_input])\n shared_api_key_input.change(update_active_llm_settings_globally,\n inputs=[shared_model_selector, shared_api_key_input], outputs=[shared_api_key_input])\n\n\n def handle_file_upload_to_preview(file_obj, file_type_label): # (保持不变)\n content_preview = f\"No {file_type_label} file selected or error.\"\n if file_obj:\n if file_type_label == \"Testbench\":\n app_state.testbench_file_path = file_obj.name\n elif file_type_label == \"Expected Output\":\n app_state.expected_output_file_path = file_obj.name\n try:\n with open(file_obj.name, \"r\", encoding=\"utf-8\") as f:\n content = f.read(2000)\n content_preview = content if content else f\"{file_type_label} empty.\"\n except Exception as e:\n content_preview = f\"Error previewing: {e}\"\n return content_preview\n\n\n design_testbench_file.upload(lambda f: handle_file_upload_to_preview(f, \"Testbench\"),\n inputs=[design_testbench_file], outputs=[design_testbench_preview])\n design_expected_output_file.upload(lambda f: handle_file_upload_to_preview(f, \"Expected Output\"),\n inputs=[design_expected_output_file], outputs=[design_expected_output_preview])\n\n\n # # === UPDATED run_workflow_gradio to follow PyQt5 WorkflowWorker agent sequence ===\n # def run_workflow_gradio(prompt, tb_file_dummy, exp_out_file_dummy, current_model_for_run, current_api_key_for_run,\n # progress=gr.Progress(track_tqdm=True)):\n # # 0. Validation and Initial UI Update (保持不变)\n # if not prompt: yield {workflow_status_display: \"Error: Design prompt required.\",\n # design_run_button: gr.update(interactive=True)}; return\n # tb_src_path = app_state.testbench_file_path\n # exp_out_src_path = app_state.expected_output_file_path\n # if not tb_src_path: yield {workflow_status_display: \"Error: Testbench file required.\",\n # design_run_button: gr.update(interactive=True)}; return\n #\n # config_module.update_llm_settings(current_model_for_run, current_api_key_for_run)\n # app_state.agent_interaction_records = [];\n # app_state.current_log_last_pos = 0\n # yield {generated_verilog_display: \"\", gate_metrics_display: \"\", workflow_status_display: \"Starting workflow...\",\n # agent_log_display_html: \"Workflow initializing...
\",\n # design_run_button: gr.update(interactive=False)}\n #\n # # 1. Setup Environment (与 PyQt5 WorkflowWorker.run() 中类似)\n # experiment_name = f\"gradio_exp_{datetime.now().strftime('%Y%m%d_%H%M%S')}\" # Original used \"gui_experiment\", making it unique\n # experiment_path = os.path.join(config_module.EXPERIMENTS_ROOT, experiment_name)\n # progress.update(0.05) # 移除 desc\n #\n # # --- File copying (参照 PyQt5 WorkflowWorker) ---\n # # setup_environment_dummy will also copy these from tb_src_path and exp_out_src_path\n # # This explicit copy here is more for directness if setup_environment_dummy doesn't do it.\n # # For now, setup_environment_dummy handles copying from these source paths.\n #\n # # --- config_module setup (参照 PyQt5 WorkflowWorker) ---\n # # config_module.update_config(self.model_name, 0) # Your actual config might have this\n # # OPENAI_API_KEY is already set by config_module.update_llm_settings\n # # config_module.REFERENCE_CODE_PATH = None # As in PyQt5\n # if hasattr(config_module, 'REFERENCE_CODE_PATH'): config_module.REFERENCE_CODE_PATH = None\n #\n # # SUMMARY_JSON_PATH (参照 PyQt5 WorkflowWorker)\n # # Ensure the directory exists for SUMMARY_JSON_PATH_TEMPLATE\n # summary_json_path_formatted = config_module.SUMMARY_JSON_PATH_TEMPLATE.format(model_name=current_model_for_run)\n # os.makedirs(os.path.dirname(summary_json_path_formatted), exist_ok=True)\n # if hasattr(config_module, 'SUMMARY_JSON_PATH'): # Check if attribute exists\n # config_module.SUMMARY_JSON_PATH = summary_json_path_formatted\n # logging.info(f\"SUMMARY_JSON_PATH set to: {summary_json_path_formatted}\")\n #\n # # Call to setup_environment (PyQt5: self.setup_environment_func)\n # try:\n # # tb_path_in_exp is the path to testbench_ref.v inside the experiment directory\n # verilog_dir, tb_path_in_exp, ref_code_path_in_exp, summary_file_in_exp, exp_output_dir = \\\n # setup_environment_dummy(experiment_path, tb_src_path, exp_out_src_path, prompt)\n #\n # app_state.current_log_file_path = config_module.EXPERIMENT_LOG_FILE # Critical for log_agent_activity\n # logging.info(\n # f\"Environment setup. Log: {app_state.current_log_file_path}. Testbench in exp: {tb_path_in_exp}\")\n #\n # # Update config_module with paths from setup_environment_dummy, as in PyQt5\n # config_module.VERILOG_DIR = verilog_dir\n # config_module.TESTBENCH_PATH = tb_path_in_exp # Crucial: use path inside experiment dir\n # config_module.REFERENCE_CODE_PATH = ref_code_path_in_exp\n # config_module.SUMMARY_FILE = summary_file_in_exp\n #\n # yield {\n # workflow_status_display: f\"Environment ready. Log: {os.path.basename(app_state.current_log_file_path)}\"}\n # except Exception as e:\n # logging.error(f\"Failed to setup environment: {e}\", exc_info=True)\n # yield {workflow_status_display: f\"Error setting up environment: {e}\",\n # design_run_button: gr.update(interactive=True)};\n # return\n # progress.update(0.15)\n #\n # # --- RAG System Initialization (参照 PyQt5 WorkflowWorker) ---\n # try:\n # # These RAG-specific configs were set directly in PyQt5 WorkflowWorker, replicate if needed\n # # For now, assuming initialize_rag_system_dummy uses values from config_module\n # rag_system = initialize_rag_system_dummy(\n # config_module.KNOWLEDGE_BASE_PATH, config_module.DETAIL_KNOWLEDGE_BASE_PATH,\n # config_module.EMBEDDING_MODEL, config_module.RAG_LLM_MODEL, # Uses workflow model\n # config_module.RAG_INDEX_PATH, config_module.RAG_DATA_PATH,\n # config_module.SUMMARY_JSON_PATH # Uses the formatted one\n # )\n # except Exception as e:\n # logging.error(f\"Failed to initialize RAG system: {e}\", exc_info=True)\n # yield {workflow_status_display: f\"Error initializing RAG: {e}\",\n # design_run_button: gr.update(interactive=True)};\n # return\n # progress.update(0.2)\n #\n # # --- Initialize Mediator and ALL Agents (参照 PyQt5 WorkflowWorker) ---\n # # These will use the global `config_module` instance, which has been updated for this run.\n # mediator = MediatorDummy() # Use ActualMediator in your real code\n # user_proxy = AgentDummy(\"UserProxy\", mediator, config_module) # rag_system not usually for UserProxy\n # coder_agent = AgentDummy(\"CoderAgent\", mediator, config_module, rag_system)\n # reviewer_agent = AgentDummy(\"Reviewer\", mediator, config_module, rag_system)\n # executor_agent = AgentDummy(\"Executor\", mediator, config_module) # No RAG for executor\n # summarizer_agent = AgentDummy(\"Summarizer\", mediator, config_module) # Summarizer might use RAG\n #\n # # Helper for UI updates (log and progress)\n # def _update_ui_and_log(current_progress_value, status_msg=\"\"):\n # new_log_records, new_log_pos, log_err = app_state.log_processor.parse_log_to_chat_records(\n # app_state.current_log_file_path, app_state.current_log_last_pos)\n # if log_err: logging.error(f\"Log parsing error: {log_err}\")\n # if new_log_records: app_state.agent_interaction_records.extend(\n # new_log_records); app_state.current_log_last_pos = new_log_pos\n # updates = {agent_log_display_html: format_chat_records_to_html(app_state.agent_interaction_records)}\n # if status_msg: updates[workflow_status_display] = status_msg\n # progress.update(current_progress_value)\n # return updates\n #\n # # --- Workflow Steps (mimicking implied sequence from user_proxy.submit_design_request) ---\n # # In PyQt5, user_proxy.submit_design_request(self.prompt) kicks off the whole chain.\n # # Here, we will make it more explicit for Gradio.\n # # Each agent \"call\" should update the mediator and log its actions.\n #\n # try:\n # # 1. UserProxy submits request\n # yield _update_ui_and_log(0.25, \"UserProxy: Submitting design request...\")\n # user_proxy.submit_design_request(prompt) # This dummy logs \"Received task\"\n #\n # # 2. CoderAgent generates code (triggered by UserProxy or Mediator)\n # yield _update_ui_and_log(0.35, \"CoderAgent: Generating code...\")\n # # In a real system, CoderAgent might be called by UserProxy or react to a Mediator message.\n # # For dummy, we call its specific method.\n # generated_code = coder_agent.submit_design_request(\n # prompt) # Reusing submit_design_request for CoderAgent's task\n # # Or, if CoderAgent had a specific method: generated_code = coder_agent.generate_code(prompt)\n # # The dummy AgentDummy's submit_design_request for CoderAgent already updates mediator.\n # latest_code_after_coder = mediator.get_latest_code()\n # final_verilog_code_output = latest_code_after_coder if latest_code_after_coder else \"CoderAgent: No code generated.\"\n # yield {**_update_ui_and_log(0.45), generated_verilog_display: final_verilog_code_output}\n #\n # # 3. ReviewerAgent reviews code\n # if latest_code_after_coder:\n # yield _update_ui_and_log(0.50, \"ReviewerAgent: Reviewing code...\")\n # # review_comments = reviewer_agent.review_code(latest_code_after_coder)\n # reviewer_agent.submit_design_request(latest_code_after_coder) # Dummy Reviewer task\n # final_verilog_code_output = mediator.get_latest_code() # Reviewer might modify/approve\n # yield {**_update_ui_and_log(0.55), generated_verilog_display: final_verilog_code_output}\n # else:\n # log_agent_activity(\"SystemWorkflow\", \"Skipping Reviewer: No code from CoderAgent.\")\n # yield _update_ui_and_log(0.55, \"Skipping Reviewer: No code from CoderAgent.\")\n #\n # # 4. ExecutorAgent executes simulation\n # # Executor needs the final code and the testbench path (config_module.TESTBENCH_PATH)\n # simulation_output_data = None\n # if final_verilog_code_output and config_module.TESTBENCH_PATH:\n # yield _update_ui_and_log(0.60, \"ExecutorAgent: Executing simulation...\")\n # # sim_data = executor_agent.execute_simulation(final_verilog_code_output, config_module.TESTBENCH_PATH)\n # executor_agent.submit_design_request(\n # {\"code\": final_verilog_code_output, \"tb_path\": config_module.TESTBENCH_PATH}) # Dummy Executor task\n # simulation_output_data = mediator.get_latest_simulation_result()\n #\n # current_sim_status_text = \"Executor: No simulation data.\"\n # if simulation_output_data and simulation_output_data.get(\"execution_result\"):\n # actual_sim_out = simulation_output_data.get(\"execution_result\", \"\")\n # sim_message = simulation_output_data.get(\"message\", \"N/A\")\n # expected_sim_out_used = simulation_output_data.get(\"expected_output_used\", \"N/A\") # From dummy\n # current_sim_status_text = f\"Executor Msg: {sim_message}\\nActual Output:\\n{actual_sim_out}\\n\"\n # current_sim_status_text += f\"\\nExpected Output (used by dummy sim):\\n{expected_sim_out_used}\\n\"\n # match_bool, compare_msg_text = compare_simulation_results(actual_sim_out, expected_sim_out_used)\n # current_sim_status_text += f\"Comparison with dummy expected: {compare_msg_text}\"\n # simulation_status_output = current_sim_status_text\n # yield {**_update_ui_and_log(0.70, simulation_status_output)}\n # else:\n # log_agent_activity(\"SystemWorkflow\", \"Skipping Executor: No code or testbench path.\")\n # simulation_status_output = \"Skipped simulation: No code or testbench.\"\n # yield _update_ui_and_log(0.70, simulation_status_output)\n #\n # # 5. Analyze Verilog for Gate Metrics (after code is potentially finalized)\n # metrics_data_for_summary = {}\n # if final_verilog_code_output:\n # module_name_for_metrics = extract_module_name(final_verilog_code_output)\n # if module_name_for_metrics:\n # # analyze_verilog_func is from PyQt5's self.analyze_verilog_func\n # # In Gradio, this is analyze_verilog_dummy or your actual function\n # metrics_data_for_summary = analyze_verilog_dummy(final_verilog_code_output, module_name_for_metrics)\n # if metrics_data_for_summary.get(\"error\"):\n # gate_metrics_output = f\"Error: {metrics_data_for_summary['error']}\"\n # else:\n # gate_metrics_output = (\n # f\"Logic Gate Count: {metrics_data_for_summary.get('logic_gate_count', 'N/A')}\\n\"\n # f\"Longest Delay: {metrics_data_for_summary.get('longest_delay', 'N/A')} ns\\n\"\n # f\"Longest Path: {' -> '.join(metrics_data_for_summary.get('longest_path', ['N/A']))}\")\n # else:\n # gate_metrics_output = \"Metrics: Could not extract module name.\"\n # yield {**_update_ui_and_log(0.75), gate_metrics_display: gate_metrics_output}\n # else:\n # gate_metrics_output = \"Metrics: No Verilog code to analyze.\"\n # yield {**_update_ui_and_log(0.75), gate_metrics_display: gate_metrics_output}\n #\n # # 6. SummarizerAgent creates summary\n # yield _update_ui_and_log(0.80, simulation_status_output + \"\\nSummarizerAgent: Generating summary...\")\n # # summary_text = summarizer_agent.summarize(final_code, sim_results, metrics)\n # summarizer_agent.submit_design_request({ # Dummy Summarizer task\n # \"code\": final_verilog_code_output, \"sim\": simulation_output_data, \"metrics\": metrics_data_for_summary\n # })\n # # final_summary_text = mediator.get_summary() # If summarizer updates mediator\n # # For dummy, it just logs. We'll use the last log message as a proxy for status.\n # simulation_status_output += \"\\nSummary generated (see logs).\"\n # yield _update_ui_and_log(0.90, simulation_status_output)\n #\n # except Exception as e_workflow_main:\n # logging.error(f\"Core workflow error: {e_workflow_main}\", exc_info=True)\n # log_agent_activity(\"SystemWorkflow\", f\"Core Workflow Exception: {e_workflow_main}\")\n # yield {**_update_ui_and_log(0.95, f\"Workflow failed: {e_workflow_main}\")}\n #\n # # Final UI update\n # progress.update(1.0) # 移除 desc\n # final_updates = _update_ui_and_log(1.0, simulation_status_output + \"\\nWorkflow complete.\")\n # final_updates[design_run_button] = gr.update(interactive=True)\n # final_updates[generated_verilog_display] = final_verilog_code_output\n # final_updates[gate_metrics_display] = gate_metrics_output\n # yield final_updates\n\n\n # 替换你现有的 run_workflow_gradio 函数\n def run_workflow_gradio(prompt_str, model_name_str, api_key_str): # 完全移除 progress 参数\n logging.info(\"--- TEST: run_workflow_gradio (ULTRA-SIMPLIFIED INPUTS, NO PROGRESS) CALLED ---\")\n print(\"--- TEST: run_workflow_gradio (ULTRA-SIMPLIFIED INPUTS, NO PROGRESS) CALLED ---\")\n\n logging.info(f\"Prompt received: '{prompt_str}'\")\n logging.info(f\"Model for run: {model_name_str}\")\n logging.info(f\"API Key for run: {'****' if api_key_str else 'None'}\")\n\n # 由于现在不是生成器,我们直接返回一个字典用于单次更新\n # 或者,如果为了与 Gradio 的 .click() 对生成器的期望保持一致(即使只 yield 一次)\n # 我们可以 yield 一次。我们先试试 yield。\n\n # 第一个 yield 会触发按钮的禁用\n yield {\n workflow_status_display: \"TEST: Button clicked! Processing (ultra-simple, no progress)...\",\n design_run_button: gr.update(interactive=False) # 确保 design_run_button 在 outputs 中\n }\n\n time.sleep(1) # 模拟一些工作\n\n logging.info(\"--- TEST: run_workflow_gradio (ULTRA-SIMPLIFIED INPUTS, NO PROGRESS) FINISHING ---\")\n # 第二个 yield 提供最终结果并重新启用按钮\n yield {\n workflow_status_display: \"TEST: Ultra-simplified (no progress) processing complete!\",\n generated_verilog_display: \"Test output (ultra-simple, no progress).\",\n # 确保 generated_verilog_display 在 outputs 中\n design_run_button: gr.update(interactive=True)\n }\n # 对于生成器,可以省略 return\n\n # ... (rest of the Gradio UI and event handlers: chat, file previews, etc. 保持不变) ...\n # ... (handle_chat_message, action_optimize_in_circuitmind, __main__ block 保持不变) ...\n\n # --- chat, file previews, and __main__ block (保持不变) ---\n def handle_chat_message(message, history, chat_model_name_from_ui, chat_api_key_from_ui):\n if not ollama_available and not (\"gpt\" in chat_model_name_from_ui.lower()):\n history.append((message, \"Chat LLM (Ollama) module not found.\"))\n return history, \"\", gr.update(visible=False), gr.update(value=\"\")\n history.append((message, None));\n yield {chat_display: history, chat_extracted_verilog_display: \"\",\n chat_optimize_button: gr.update(visible=False), chat_message_input: \"\"}\n try:\n response_content = \"\"\n if \"gpt\" in chat_model_name_from_ui.lower() or \"openai\" in chat_model_name_from_ui.lower():\n api_key_to_use = chat_api_key_from_ui if chat_api_key_from_ui else config_module.get_current_api_key_value(\n chat_model_name_from_ui)\n if not api_key_to_use: raise ValueError(f\"API key for {chat_model_name_from_ui} not found.\")\n logging.info(f\"Calling OpenAI-like model: {chat_model_name_from_ui}\");\n # Actual OpenAI call placeholder\n response_content = f\"(Simulated OpenAI for '{chat_model_name_from_ui}'): You said: {message}\"\n elif ollama_available:\n logging.info(f\"Calling Ollama model: {chat_model_name_from_ui}\")\n response = ollama.chat(model=chat_model_name_from_ui, messages=[{\"role\": \"user\", \"content\": message}])\n response_content = response['message']['content']\n else:\n raise ConnectionError(\"Ollama not available and model not recognized.\")\n history[-1] = (message, response_content)\n verilog_match = re.search(r'```verilog\\n([\\s\\S]*?)\\n```', response_content, re.MULTILINE)\n extracted_code = verilog_match.group(1).strip() if verilog_match else \"\"\n yield {chat_display: history, chat_extracted_verilog_display: extracted_code,\n chat_optimize_button: gr.update(visible=bool(extracted_code)), chat_message_input: \"\"}\n except Exception as e:\n logging.error(f\"Chat API error for {chat_model_name_from_ui}: {e}\", exc_info=True);\n history[-1] = (message, f\"Error: {str(e)}\")\n yield {chat_display: history, chat_extracted_verilog_display: \"\",\n chat_optimize_button: gr.update(visible=False), chat_message_input: \"\"}\n\n\n chat_message_input.submit(handle_chat_message,\n inputs=[chat_message_input, chat_display, shared_model_selector, shared_api_key_input],\n outputs=[chat_display, chat_extracted_verilog_display, chat_optimize_button,\n chat_message_input])\n chat_send_button.click(handle_chat_message,\n inputs=[chat_message_input, chat_display, shared_model_selector, shared_api_key_input],\n outputs=[chat_display, chat_extracted_verilog_display, chat_optimize_button,\n chat_message_input])\n\n\n # def action_optimize_in_circuitmind(extracted_verilog_code):\n # return {design_prompt_input: extracted_verilog_code, tabs: gr.update(selected=0)}\n\n\n def action_optimize_in_circuitmind(extracted_verilog_code):\n logging.info(f\"Populating Design Prompt with code: {extracted_verilog_code[:50]}...\")\n return {\n design_prompt_input: gr.update(value=extracted_verilog_code, interactive=True),\n tabs: gr.update(selected=0) # <-- 暂时注释掉这行\n }\n\n chat_optimize_button.click(action_optimize_in_circuitmind, inputs=[chat_extracted_verilog_display],\n outputs=[design_prompt_input, tabs])\n\nif __name__ == \"__main__\":\n icons_dir = \"icons\";\n os.makedirs(icons_dir, exist_ok=True) # (Dummy icon creation logic 保持不变)\n # ... (The rest of the dummy icon creation logic from the previous response)\n original_get_avatar = LogProcessor.get_avatar # Monkey patch for Gradio file serving\n\n\n def gradio_get_avatar(self, agent_name):\n res = original_get_avatar(self, agent_name)\n if res[\"type\"] == \"image\" and res[\"value\"]: res[\"value\"] = os.path.abspath(res[\"value\"]).replace(os.sep, '/')\n return res\n\n\n LogProcessor.get_avatar = gradio_get_avatar\n demo.queue().launch(share=False, server_port=7860,\n allowed_paths=[os.path.abspath(icons_dir)])\n"}}, "GUI/Gradio_ui_5.py": {"4677b755a718f5b15fc304ba3866a8e04a9a2902": {"t": 1785595589.2156258, "n": 52927, "h": "161f8cd77ce94235", "c": "import gradio as gr\nimport sys\nimport os\nimport logging\nimport re\nimport time\nimport html\nfrom datetime import datetime\nimport shutil # For file operations in workflow\n\n# Assuming ollama is installed for the chat functionality\ntry:\n import ollama\n\n ollama_available = True\nexcept ImportError:\n logging.warning(\"ollama module not found. Chat functionality will be limited.\")\n ollama_available = False\n\n# --- Configuration and Constants (from original PyQt5 code) ---\nAGENT_AVATARS = {\n \"UserProxy\": {\"path\": \"icons/user.png\", \"emoji\": \"😊\"},\n \"CoderAgent\": {\"path\": \"icons/coder.png\", \"emoji\": \"💻\"},\n \"Reviewer\": {\"path\": \"icons/reviewer.png\", \"emoji\": \"🔍\"},\n \"Executor\": {\"path\": \"icons/executor.png\", \"emoji\": \"⚙️\"},\n \"Summarizer\": {\"path\": \"icons/summarizer.png\", \"emoji\": \"📝\"},\n \"root\": {\"path\": \"icons/root.png\", \"emoji\": \"🌟\"},\n \"RAG\": {\"path\": \"icons/rag.png\", \"emoji\": \"📚\"},\n \"Mediator\": {\"path\": \"icons/mediator.png\", \"emoji\": \"🤝\"},\n \"ChatSession-coder\": {\"path\": \"icons/chatsession-coder.png\", \"emoji\": \"🖥️\"},\n \"ChatSession-reviewer\": {\"path\": \"icons/chatsession-reviewer.png\", \"emoji\": \"🕵️\"},\n \"transitions.core\": {\"path\": \"icons/transitions-core.png\", \"emoji\": \"🔄\"},\n \"httpx\": {\"path\": \"icons/httpx.png\", \"emoji\": \"🌐\"},\n \"default\": {\"path\": \"icons/default.png\", \"emoji\": \"❓\"}\n}\n\n# --- Logging Setup ---\nlogging.basicConfig(stream=sys.stdout, level=logging.INFO,\n # Changed to INFO for Gradio, DEBUG is too verbose for typical use\n format='[%(asctime)s] [%(levelname)s] [%(name)s:%(lineno)d] %(message)s')\n\n\n# --- Utility Functions (from original PyQt5 code or adapted) ---\nclass LogProcessor:\n def __init__(self, log_format_regex, agent_format_regex, timestamp_format_str=None,\n avatar_dir=\"avatars_gradio/resized\"):\n self.log_format = re.compile(log_format_regex, re.MULTILINE)\n self.agent_format = re.compile(agent_format_regex) # This regex might be too specific\n self.timestamp_format = timestamp_format_str\n self.avatar_dir = avatar_dir\n os.makedirs(self.avatar_dir, exist_ok=True)\n self.color_code_regex = re.compile(r'\\x1B\\[[0-9;]*[mGKHfJABCD]?')\n self.current_conversation = None\n self.current_agent = None\n self.current_timestamp = None\n self.current_log_level = None\n self.message_buffer = []\n self.avatar_cache = {} # For resized avatars\n\n def remove_color_codes(self, text):\n return self.color_code_regex.sub('', text)\n\n def clean_message(self, message):\n message = self.remove_color_codes(message)\n message = message.strip()\n return message if message else None\n\n def get_avatar(self, agent_name):\n avatar_info = AGENT_AVATARS.get(agent_name, AGENT_AVATARS[\"default\"])\n avatar_path = avatar_info[\"path\"]\n emoji = avatar_info[\"emoji\"]\n\n # In Gradio, ensure paths are web-accessible or use emojis.\n # For local image files, they need to be in a directory Gradio can serve\n # or use absolute paths if your environment allows.\n # For simplicity, we'll primarily rely on emoji if path doesn't resolve easily,\n # or assume `avatar_path` can be made web-accessible (e.g. by placing `icons` in cwd).\n if os.path.exists(avatar_path): # Check if original icon exists\n # Resizing logic (simplified for Gradio, as direct QPixmap not available)\n # We'll assume icons are already reasonably sized or use original if accessible.\n # The HTML will refer to the original path for now.\n return {\"type\": \"image\", \"value\": avatar_path, \"emoji\": emoji}\n return {\"type\": \"emoji\", \"value\": emoji}\n\n def flush_buffer(self, chat_records):\n if not self.message_buffer or not self.current_agent:\n self.message_buffer = []\n return\n\n message = '\\n'.join(self.message_buffer)\n cleaned_message = self.clean_message(message)\n if cleaned_message:\n formatted_time = self.current_timestamp or datetime.now().strftime('%Y-%m-%d %H:%M:%S')\n try:\n if self.current_timestamp and self.timestamp_format:\n time_obj = datetime.strptime(self.current_timestamp, self.timestamp_format)\n formatted_time = time_obj.strftime('%Y-%m-%d %H:%M:%S')\n except (ValueError, TypeError) as e:\n logging.warning(f\"Timestamp parsing failed ({e}), using raw: {self.current_timestamp}\")\n\n avatar = self.get_avatar(self.current_agent)\n record = {\n 'time': formatted_time,\n 'agent': self.current_agent,\n 'avatar': avatar,\n 'message': cleaned_message\n }\n chat_records.append(record)\n self.message_buffer = []\n\n def parse_log_to_chat_records(self, log_file_path, last_position):\n chat_records = []\n error_message = None\n new_position = last_position\n\n if not log_file_path or not os.path.exists(log_file_path):\n return chat_records, new_position, \"Log file does not exist.\"\n if os.path.getsize(log_file_path) == 0:\n return chat_records, new_position, \"Log file is empty.\"\n\n try:\n with open(log_file_path, 'r', encoding='utf-8') as f:\n f.seek(last_position)\n content = f.read()\n new_position = f.tell()\n\n if not content:\n return chat_records, new_position, None # No new content\n\n cleaned_content = self.remove_color_codes(content) # Clean once\n lines = cleaned_content.splitlines()\n\n except Exception as e:\n return chat_records, last_position, f\"Error reading log file: {str(e)}\"\n\n for line in lines:\n line = line.strip()\n if not line:\n continue\n\n match = self.log_format.match(line)\n if match:\n self.flush_buffer(chat_records) # Flush previous buffered message\n log_data = match.groupdict()\n timestamp = log_data.get('timestamp', '')\n log_level = log_data.get('log_level', '').upper()\n agent_name = log_data.get('agent_name', '')\n message_part = log_data.get('message', '').strip()\n\n # Filter for relevant agents/levels if necessary (original code had a filter)\n if log_level not in ['DIALOGUE', 'INFO', 'DEBUG'] or not AGENT_AVATARS.get(agent_name):\n # If it's not a recognized agent log line, but we had a current agent,\n # append this line to its buffer as continuation.\n if self.current_agent and message_part: # Add current line as part of previous message\n self.message_buffer.append(line) # Append the raw line for context\n continue\n\n self.current_agent = agent_name\n self.current_timestamp = timestamp\n self.current_log_level = log_level\n if message_part:\n self.message_buffer.append(message_part)\n\n elif self.current_agent: # Line does not match main log format, but we have an active agent\n # This part handles multi-line messages\n self.message_buffer.append(line)\n # else:\n # Lines that don't match and no current agent are ignored or handled by agent_format\n # The agent_format regex was: r'^\\s*, agent=(?PNo agent interactions logged yet.
\"\n\n html_parts = [\n \"\",\n \"Agent interactions will appear here...
\")\n\n with gr.TabItem(\"ReasoningV (Chat with LLM)\", id=1):\n chat_display = gr.Chatbot(label=\"Chat with ReasoningV\", height=500)\n chat_message_input = gr.Textbox(label=\"Your Message\", placeholder=\"Type your message and press Enter...\")\n chat_send_button = gr.Button(\"Send\", variant=\"primary\")\n\n gr.Markdown(\"### Extracted Verilog from Chat\")\n chat_extracted_verilog_display = gr.Code(label=\"Verilog Code from Conversation\",\n interactive=False)\n chat_optimize_button = gr.Button(\"Optimize this Verilog in CircuitMind\", visible=False)\n\n\n # --- Event Handlers & Backend Logic ---\n\n # Shared Model/API Key Update Logic\n def update_active_llm_settings(model_name, api_key):\n app_state.active_model_name = model_name\n app_state.active_api_key = api_key if api_key else config_module.get_current_api_key(model_name)\n config_module.update_llm_settings(app_state.active_model_name,\n app_state.active_api_key) # Update backend config\n logging.info(\n f\"Global LLM settings updated: Model={app_state.active_model_name}, Key={'Set' if app_state.active_api_key else 'Not Set/Default'}\")\n # If you need to update other components (e.g. if there were per-tab selectors) do it here\n return {shared_api_key_input: app_state.active_api_key} # Update displayed API key\n\n\n shared_model_selector.change(update_active_llm_settings, inputs=[shared_model_selector, shared_api_key_input],\n outputs=[shared_api_key_input])\n shared_api_key_input.change(update_active_llm_settings, inputs=[shared_model_selector, shared_api_key_input],\n outputs=[shared_api_key_input])\n\n\n # CircuitMind (Design) Tab Logic\n def handle_testbench_upload(file_obj):\n if file_obj:\n app_state.testbench_file_path = file_obj.name # Gradio gives a temp path\n try:\n with open(file_obj.name, \"r\", encoding=\"utf-8\") as f:\n content = f.read()\n return content[:2000] # Preview limit\n except Exception as e:\n logging.error(f\"Error reading testbench for preview: {e}\")\n return f\"Error previewing file: {e}\"\n return \"No file or error.\"\n\n\n design_testbench_file.upload(handle_testbench_upload, inputs=[design_testbench_file],\n outputs=[design_testbench_preview])\n\n\n def handle_expected_output_upload(file_obj):\n if file_obj:\n app_state.expected_output_file_path = file_obj.name\n try:\n with open(file_obj.name, \"r\", encoding=\"utf-8\") as f:\n content = f.read()\n return content[:2000] # Preview limit\n except Exception as e:\n logging.error(f\"Error reading expected output for preview: {e}\")\n return f\"Error previewing file: {e}\"\n return \"No file or error.\"\n\n\n design_expected_output_file.upload(handle_expected_output_upload, inputs=[design_expected_output_file],\n outputs=[design_expected_output_preview])\n\n\n def run_workflow_gradio(prompt, tb_file_obj, exp_out_file_obj, current_model, current_api_key,\n progress=gr.Progress(track_tqdm=True)):\n # 0. Initial Setup and Validation\n if not prompt: yield {workflow_status_display: \"Error: Design prompt is required.\",\n design_run_button: gr.update(interactive=True)}; return\n if not tb_file_obj: yield {workflow_status_display: \"Error: Testbench file is required.\",\n design_run_button: gr.update(interactive=True)}; return\n\n # Use selected model/key for this run explicitly\n config_module.update_llm_settings(current_model, current_api_key)\n\n # Clear previous run's state / displays\n app_state.agent_interaction_records = []\n app_state.current_log_last_pos = 0\n yield {\n generated_verilog_display: \"\",\n gate_metrics_display: \"\",\n workflow_status_display: \"Starting workflow...\",\n agent_log_display_html: \"Workflow initializing...
\",\n design_run_button: gr.update(interactive=False) # Disable run button\n }\n\n # 1. Setup Experiment Environment (Paths, Directories)\n experiment_name = f\"gradio_exp_{datetime.now().strftime('%Y%m%d_%H%M%S')}\"\n experiment_path = os.path.join(config_module.EXPERIMENTS_ROOT, experiment_name)\n progress.update(0.1)\n\n # Ensure app_state paths for uploaded files are used if available\n tb_src_path = tb_file_obj.name if tb_file_obj else None\n exp_out_src_path = exp_out_file_obj.name if exp_out_file_obj else None\n\n try:\n # setup_environment_dummy now also handles log file path in config_module\n verilog_dir, tb_path_in_exp, _, summary_file_in_exp, exp_out_dir = \\\n setup_environment_dummy(experiment_path, tb_src_path, exp_out_src_path, prompt)\n app_state.current_log_file_path = config_module.EXPERIMENT_LOG_FILE # Get log path from config\n logging.info(\n f\"Experiment environment set up in: {experiment_path}. Log file: {app_state.current_log_file_path}\")\n yield {\n workflow_status_display: f\"Environment ready. Log: {os.path.basename(app_state.current_log_file_path)}\"}\n except Exception as e:\n logging.error(f\"Failed to setup environment: {e}\", exc_info=True)\n yield {workflow_status_display: f\"Error setting up environment: {e}\",\n design_run_button: gr.update(interactive=True)};\n return\n\n progress.update(0.2)\n # 2. Initialize Backend Systems (RAG, Mediator, Agents) - Using Dummies\n try:\n rag_system = initialize_rag_system_dummy(\n config_module.KNOWLEDGE_BASE_PATH, config_module.DETAIL_KNOWLEDGE_BASE_PATH,\n config_module.EMBEDDING_MODEL, config_module.RAG_LLM_MODEL, # Main LLM\n config_module.RAG_INDEX_PATH, config_module.RAG_DATA_PATH,\n config_module.SUMMARY_JSON_PATH_TEMPLATE.format(model_name=current_model)\n )\n mediator = MediatorDummy()\n # Pass the global config_module to agents\n user_proxy = AgentDummy(\"UserProxy\", mediator, config_module, rag_system)\n # Other agents would be initialized here too if used directly in this function:\n # coder_agent = AgentDummy(\"CoderAgent\", mediator, config_module, rag_system)\n # reviewer = AgentDummy(\"Reviewer\", mediator, config_module, rag_system)\n # executor = AgentDummy(\"Executor\", mediator, config_module) # No RAG for executor\n # summarizer = AgentDummy(\"Summarizer\", mediator, config_module)\n except Exception as e:\n logging.error(f\"Failed to initialize backend systems: {e}\", exc_info=True)\n yield {workflow_status_display: f\"Error initializing backend: {e}\",\n design_run_button: gr.update(interactive=True)};\n return\n\n # 3. Run the Core Workflow (e.g., user_proxy.submit_design_request)\n # This part will generate log entries that we need to parse and display\n main_task_description = \"Running main agent workflow...\"\n progress.update(0.3)\n yield {workflow_status_display: main_task_description}\n\n # Start the process that writes to the log\n try:\n # In a real scenario, this might be a non-blocking call or a complex sequence.\n # For the dummy, it's synchronous but writes to the log via log_agent_activity.\n user_proxy.submit_design_request(prompt) # This will trigger dummy agent interactions\n except Exception as e:\n logging.error(f\"Error during agent workflow execution: {e}\", exc_info=True)\n log_agent_activity(\"System\", f\"Workflow error: {e}\")\n # Continue to try and display logs and partial results if any\n\n # 4. Periodically Update Agent Log Display and Check for Final Results\n # The dummy agents log synchronously. In a real async scenario, this loop would poll.\n # For this refactor, we'll simulate reading the log after the dummy process.\n # A true streaming approach would involve the backend agents writing to the log,\n # and this loop `yield`ing updates by reading it.\n\n # Since dummy logs are written synchronously within submit_design_request,\n # we process the entire log after it finishes.\n # For a real long-running process, you'd put `user_proxy.submit_design_request`\n # in a separate thread/process and poll the log file here in a loop with sleeps.\n\n # Update agent log display\n new_records, new_pos, _ = app_state.log_processor.parse_log_to_chat_records(app_state.current_log_file_path,\n app_state.current_log_last_pos)\n if new_records:\n app_state.agent_interaction_records.extend(new_records)\n app_state.current_log_last_pos = new_pos\n log_html = format_chat_records_to_html(app_state.agent_interaction_records)\n yield {agent_log_display_html: log_html}\n progress.update(0.7)\n\n # 5. Retrieve and Display Final Results\n progress.update(0.8)\n final_verilog_code = mediator.get_latest_code()\n simulation_output_data = mediator.get_latest_simulation_result()\n\n output_updates = {}\n if final_verilog_code:\n output_updates[generated_verilog_display] = final_verilog_code\n module_name = extract_module_name(final_verilog_code)\n if module_name:\n metrics = analyze_verilog_dummy(final_verilog_code, module_name) # Use dummy\n metrics_str = f\"Logic Gate Count: {metrics.get('logic_gate_count', 'N/A')}\\n\" \\\n f\"Longest Delay: {metrics.get('longest_delay', 'N/A')} ns\\n\" \\\n f\"Longest Path: {' -> '.join(metrics.get('longest_path', ['N/A']))}\"\n if metrics.get(\"error\"): metrics_str = f\"Error: {metrics['error']}\"\n output_updates[gate_metrics_display] = metrics_str\n else:\n output_updates[gate_metrics_display] = \"Could not extract module name for analysis.\"\n else:\n output_updates[generated_verilog_display] = \"No Verilog code generated.\"\n output_updates[gate_metrics_display] = \"No Verilog code to analyze.\"\n\n sim_status_text = \"Simulation results:\\n\"\n if simulation_output_data and simulation_output_data.get(\"execution_result\"):\n actual_sim_out = simulation_output_data.get(\"execution_result\", \"\")\n sim_message = simulation_output_data.get(\"message\", \"No specific message.\")\n sim_status_text += f\"Message: {sim_message}\\nActual Output:\\n{actual_sim_out}\\n\"\n\n # Compare with expected output if available\n expected_output_content = \"Expected output not loaded.\"\n if exp_out_src_path: # If a file was provided\n try:\n with open(exp_out_src_path, 'r', encoding='utf-8') as f:\n expected_output_content = f.read().strip()\n sim_status_text += f\"\\nExpected Output:\\n{expected_output_content}\\n\"\n match, compare_msg = compare_simulation_results(actual_sim_out, expected_output_content)\n sim_status_text += f\"Comparison: {compare_msg}\"\n except Exception as e:\n sim_status_text += f\"Could not read/compare expected output: {e}\"\n else:\n sim_status_text += \"No expected output file provided for comparison.\"\n else:\n sim_status_text += \"No simulation output found.\"\n\n output_updates[workflow_status_display] = sim_status_text\n progress.update(1.0)\n output_updates[design_run_button] = gr.update(interactive=True) # Re-enable button\n yield output_updates\n\n\n design_run_button.click(\n run_workflow_gradio,\n inputs=[design_prompt_input, design_testbench_file, design_expected_output_file, shared_model_selector,\n shared_api_key_input],\n outputs=[generated_verilog_display, gate_metrics_display, workflow_status_display, agent_log_display_html,\n design_run_button]\n )\n\n\n # ReasoningV (Chat) Tab Logic\n def handle_chat_message(message, history, current_model, current_api_key):\n if not ollama_available:\n history.append((message, \"Ollama module not found. Cannot connect to LLM.\"))\n return history, \"\", gr.update(visible=False)\n\n history.append((message, None)) # Show user message, bot thinking\n yield {chat_display: history, chat_extracted_verilog_display: \"\",\n chat_optimize_button: gr.update(visible=False)}\n\n # Use selected model/key for this chat explicitly\n # Note: ollama.chat uses 'model' parameter directly, not a global API key for different models usually.\n # API key handling might be different if not using Ollama or if Ollama is behind a proxy.\n # For now, assume model name is sufficient for ollama.chat. API key might be for OpenAI models.\n # The `current_api_key` from shared input is more relevant if calling OpenAI or similar.\n\n try:\n # Check if model is OpenAI-like or Ollama-like based on name\n if \"gpt\" in current_model.lower() or \"openai\" in current_model.lower():\n # This part would require openai python client and correct API key setup\n # For now, this is a placeholder for non-ollama models\n response_content = f\"Model '{current_model}' would need specific API call (e.g. OpenAI). Key: {'Set' if current_api_key else 'Not set'}.\"\n logging.warning(\"Attempted to use non-Ollama model; specific client needed.\")\n\n else: # Assume Ollama model\n response = ollama.chat(\n model=current_model,\n messages=[{\"role\": \"user\", \"content\": message}] # Simpler history for now\n # To use full history: messages=[{\"role\": \"user/assistant\", \"content\": h_msg} for h_msg in flat_history]\n )\n response_content = response['message']['content']\n\n history[-1] = (message, response_content) # Update with bot response\n\n # Extract Verilog\n verilog_code_match = re.search(r'```verilog\\n([\\s\\S]*?)\\n```', response_content, re.MULTILINE)\n extracted_code = \"\"\n optimize_btn_visible = False\n if verilog_code_match:\n extracted_code = verilog_code_match.group(1).strip()\n optimize_btn_visible = True\n\n yield {\n chat_display: history,\n chat_extracted_verilog_display: extracted_code,\n chat_optimize_button: gr.update(visible=optimize_btn_visible)\n }\n\n except Exception as e:\n logging.error(f\"Error during chat API call: {e}\", exc_info=True)\n history[-1] = (message, f\"Error: {str(e)}\")\n yield {chat_display: history, chat_extracted_verilog_display: \"\",\n chat_optimize_button: gr.update(visible=False)}\n\n\n chat_submit_trigger = chat_message_input.submit(\n handle_chat_message,\n inputs=[chat_message_input, chat_display, shared_model_selector, shared_api_key_input],\n outputs=[chat_display, chat_extracted_verilog_display, chat_optimize_button]\n )\n chat_send_button.click(\n handle_chat_message,\n inputs=[chat_message_input, chat_display, shared_model_selector, shared_api_key_input],\n outputs=[chat_display, chat_extracted_verilog_display, chat_optimize_button]\n )\n # After message is processed, clear input\n chat_submit_trigger.then(lambda: gr.update(value=\"\"), outputs=[chat_message_input])\n # chat_send_button.click event already clears input via the .then() on submit_trigger if wired similarly,\n # or add another .then() to chat_send_button.click's output if needed.\n # For simplicity, let's make send_button also clear:\n chat_send_button.click(lambda: gr.update(value=\"\"), outputs=[chat_message_input])\n\n\n def action_optimize_in_circuitmind(extracted_verilog_code):\n # Switch to Design Tab and populate prompt\n # Gradio doesn't have a direct tab switch function from Python.\n # The user has to click. We can update the content of the target tab.\n return {\n design_prompt_input: extracted_verilog_code,\n tabs: gr.update(selected=0) # Request to switch to tab with id 0\n }\n\n\n chat_optimize_button.click(\n action_optimize_in_circuitmind,\n inputs=[chat_extracted_verilog_display],\n outputs=[design_prompt_input, tabs] # Update prompt on Design tab and try to switch\n )\n\nif __name__ == \"__main__\":\n # Create dummy icon files if they don't exist for testing avatar paths\n os.makedirs(\"icons\", exist_ok=True)\n icon_files_to_check = [\"user.png\", \"coder.png\", \"reviewer.png\", \"executor.png\", \"summarizer.png\", \"default.png\"]\n for icon_name in icon_files_to_check:\n dummy_icon_path = os.path.join(\"icons\", icon_name)\n if not os.path.exists(dummy_icon_path):\n try:\n from PIL import Image # type: ignore\n\n img = Image.new('RGB', (32, 32), color='red')\n img.save(dummy_icon_path)\n logging.info(f\"Created dummy icon: {dummy_icon_path}\")\n except ImportError:\n logging.warning(\n f\"Pillow not installed. Cannot create dummy icon {dummy_icon_path}. Avatars might not load images.\")\n except Exception as e_img:\n logging.error(f\"Error creating dummy icon {dummy_icon_path}: {e_img}\")\n\n demo.queue().launch(share=False, server_port=7860)"}}}}