Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/123fjh_circuitmind.json
T

1 line
137 KiB
JSON

{"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 \"<p>No agent interactions logged yet.</p>\"\n html_parts = [\n \"<style>body { font-family: Arial, sans-serif; margin: 0px; } .message-container { margin-bottom: 12px; display: flex; align-items: flex-start; padding: 5px;} .avatar-container { width: 32px; height: 32px; margin-right: 8px; flex-shrink: 0; display:flex; align-items:center; justify-content:center; } .avatar-img { width: 30px; height: 30px; border-radius: 50%; } .avatar-emoji { font-size: 24px; } .content-container { display: flex; flex-direction: column; max-width: calc(100% - 50px); } .header { display: flex; align-items: center; margin-bottom: 3px; } .agent-name { font-weight: bold; margin-right: 8px; font-size: 0.9em; color: #333; } .timestamp { color: #777; font-size: 0.8em; } .bubble { background-color: #f0f0f0; border-radius: 8px; padding: 8px 10px; font-size: 0.95em; color: #333; white-space: pre-wrap; word-wrap: break-word; box-shadow: 0 1px 1px rgba(0,0,0,0.05); } </style><div>\"]\n for record in chat_records_list:\n escaped_message = html.escape(record['message']);\n agent_name_html = html.escape(record['agent']);\n timestamp_html = html.escape(record['time']);\n avatar_data = record.get('avatar', {})\n html_parts.append('<div class=\"message-container\"><div class=\"avatar-container\">')\n if avatar_data.get('type') == 'image' and avatar_data.get('value') and os.path.exists(\n avatar_data['value'].replace('file:///', '')): # Check local path if using file:///\n # Gradio's `file=` prefix is for absolute paths.\n # The path from get_avatar is already absolute and /-formatted.\n html_parts.append(f'<img src=\"file={avatar_data[\"value\"]}\" class=\"avatar-img\" alt=\"{agent_name_html}\">')\n else:\n emoji_val = avatar_data.get('value', AGENT_AVATARS[\"default\"][\"emoji\"])\n html_parts.append(f'<span class=\"avatar-emoji\">{emoji_val}</span>')\n html_parts.append('</div><div class=\"content-container\"><div class=\"header\">')\n html_parts.append(\n f'<span class=\"agent-name\">{agent_name_html}</span><span class=\"timestamp\">[{timestamp_html}]</span></div>')\n html_parts.append(f'<div class=\"bubble\">{escaped_message}</div></div></div>')\n html_parts.append(\"</div>\");\n return \"\".join(html_parts)\n\n\ndef compare_simulation_results(actual_output, expected_output): # (保持不变)\n actual_lines = actual_output.strip().split('\\n');\n actual_results = []\n for line in actual_lines:\n match = re.match(r\"Time=\\d+:\\s*in=(\\d),\\s*out=(\\d)\", line.strip())\n if match: actual_results.append((match.group(1), match.group(2)))\n expected_lines = expected_output.strip().split('\\n');\n expected_results = []\n for line in expected_lines:\n match = re.match(r\"Time=\\d+:\\s*in=(\\d),\\s*out=(\\d)\", line.strip())\n if match: expected_results.append((match.group(1), match.group(2)))\n if not actual_results and not expected_results and actual_output.strip() == expected_output.strip(): return True, \"Outputs match (non-structured).\"\n if not actual_results and not expected_results and actual_output.strip() != expected_output.strip(): return False, f\"Output format mismatch or one output is empty. Actual: {len(actual_results)} structured, Expected: {len(expected_results)} structured.\"\n if not actual_results and expected_results: return False, \"Actual output empty/wrong format, expected has data.\"\n if actual_results and not expected_results: return False, \"Expected output empty/wrong format, actual has data.\"\n if len(actual_results) != len(\n expected_results): return False, f\"Output length mismatch: expected {len(expected_results)}, got {len(actual_results)}\"\n for i, (actual, expected) in enumerate(zip(actual_results, expected_results)):\n if actual != expected: return False, f\"Mismatch at line {i + 1}: expected in={expected[0]}, out={expected[1]}; got in={actual[0]}, out={actual[1]}\"\n return True, \"All structured tests passed.\"\n\n\ndef extract_module_name(verilog_code): # (保持不变)\n if not verilog_code: return None\n match = re.search(r'module\\s+(\\w+)\\s*\\(', verilog_code);\n return match.group(1) if match else None\n\n\n# --- Configuration Module (MockConfigModule - 保持不变,但其属性将被正确使用) ---\nclass MockConfigModule:\n EXPERIMENTS_ROOT = \"./experiments_gradio\";\n OPENAI_API_KEY = \"\";\n KNOWLEDGE_BASE_PATH = './knowledge_base/RAG-data'\n DETAIL_KNOWLEDGE_BASE_PATH = './knowledge_base/RAG-data-detail';\n EMBEDDING_MODEL = 'nomic-embed-text';\n RAG_LLM_MODEL = \"\"\n RAG_INDEX_PATH = './knowledge_base/RAG-data/vector_index.faiss';\n RAG_DATA_PATH = './knowledge_base/RAG-data/vector_data.json'\n SUMMARY_JSON_PATH_TEMPLATE = \"./knowledge_base/Model-incrementment/{model_name}.json\"\n VERILOG_DIR = \"\";\n TESTBENCH_PATH = \"\";\n REFERENCE_CODE_PATH = None;\n SUMMARY_FILE = \"\";\n EXPERIMENT_LOG_FILE = \"\"\n api_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\": [\n \"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\"],\n \"BASE_URL\": \"https://api.openai.com/v1\",\n },\n \"gpt-4o-mini\": {\n \"API_KEY\": [\n \"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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\",\n \"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 \"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 default_model = \"qwen2.5-coder:14b\"\n\n def __init__(self):\n os.makedirs(self.EXPERIMENTS_ROOT, exist_ok=True)\n if self.SUMMARY_JSON_PATH_TEMPLATE and \"{model_name}\" in self.SUMMARY_JSON_PATH_TEMPLATE: # Ensure dirname works\n summary_dir = os.path.dirname(self.SUMMARY_JSON_PATH_TEMPLATE.format(model_name=\"any_model\"))\n if summary_dir: os.makedirs(summary_dir, exist_ok=True)\n if self.KNOWLEDGE_BASE_PATH: os.makedirs(self.KNOWLEDGE_BASE_PATH, exist_ok=True)\n self.RAG_LLM_MODEL = self.default_model\n self.OPENAI_API_KEY = self.get_current_api_key_value(self.default_model) # Store actual key string or list\n\n def get_current_api_key_value(self, model_name_str): # Renamed to avoid confusion with original\n return self.api_configs.get(model_name_str, {}).get(\"API_KEY\", \"\") # Use \"API_KEY\" as in your config\n\n def update_llm_settings(self, model_name, api_key_override_value):\n self.RAG_LLM_MODEL = model_name\n if api_key_override_value: # Assuming api_key_override_value is the key string itself\n self.OPENAI_API_KEY = api_key_override_value\n logging.info(f\"Config updated: Workflow Model={model_name}. Using OVERRIDE API Key.\")\n else:\n self.OPENAI_API_KEY = self.get_current_api_key_value(model_name)\n logging.info(\n f\"Config updated: Workflow Model={model_name}. Using configured API Key: {'********' if self.OPENAI_API_KEY else 'None/NotNeeded'}.\")\n\n\nconfig_module = MockConfigModule()\n\n\n# --- Dummy Backend Classes (修改 AgentDummy 以更好地反映 PyQt5 WorkflowWorker 中的 Agent 角色) ---\n# PyQt5 WorkflowWorker 使用 self.UserProxy_class, self.CoderAgent_class 等,\n# 它们在 ImportError 时都指向同一个 _dummy_agent 类型。\n# 我们用 AgentDummy 代表这个通用的 _dummy_agent 类型。\n# 关键在于 run_workflow_gradio 中如何实例化和使用它来代表不同的角色。\n\nclass MediatorDummy: # (与之前相同)\n def __init__(self): self._latest_code = None; self._latest_sim_result = None; self._summary = None; logging.info(\n \"MediatorDummy initialized.\")\n\n def get_latest_code(self, agent_name=\"\", message=\"\"): return self._latest_code\n\n def get_latest_simulation_result(self, agent_name=\"\", message=\"\"): return self._latest_sim_result\n\n def set_code(self, code, agent=\"CoderAgent\"): self._latest_code = code; log_agent_activity(agent,\n f\"Submitted code snippet:\\n{code[:100]}...\")\n\n def set_simulation_result(self, result, agent=\"Executor\"): self._latest_sim_result = result; log_agent_activity(\n agent, f\"Submitted simulation result: {result.get('message', 'No message')}\")\n\n def set_summary(self, summary_text, agent=\"Summarizer\"): self._summary = summary_text; log_agent_activity(agent,\n f\"Submitted summary: {summary_text[:100]}...\")\n\n def get_summary(self): return self._summary\n\n\nclass AgentDummy: # 代表 PyQt5 中的 _dummy_agent\n def __init__(self, name, mediator, config, rag_system=None):\n self.name = name\n self.mediator = mediator\n self.config = config # This is the config_module instance\n self.rag_system = rag_system\n # Agent 知道它应该使用的模型和 API 密钥来自 self.config\n logging.info(\n f\"AgentDummy '{self.name}' initialized. Configured LLM for workflow: '{self.config.RAG_LLM_MODEL}'.\")\n log_agent_activity(self.name, f\"Initialized (Workflow LLM: {self.config.RAG_LLM_MODEL})\")\n\n def submit_design_request(self, prompt_or_data):\n # 这是 UserProxy 的主要入口点,或者其他 Agent 处理特定任务的方法。\n # 在 PyQt5 WorkflowWorker.run() 中,首先调用 user_proxy.submit_design_request(self.prompt)\n # 这个调用启动了整个流程。\n # 对于 Gradio,我们将模拟这个启动,然后逐步执行其他 Agent 的步骤。\n log_agent_activity(self.name, f\"Received task/prompt: '{str(prompt_or_data)[:70]}...'\")\n\n # ---- 这个 AgentDummy 的行为需要根据其 'name' 来模拟 PyQt5 中特定 Agent 的行为 ----\n # ---- 或者,更准确地,run_workflow_gradio 将显式调用代表特定 Agent 的方法 ----\n\n if self.name == \"UserProxy\":\n # UserProxy 通常只是启动流程,具体工作由其他 Agent 完成。\n # 在 PyQt5 中,它调用 self.config_module.update_config 等。\n # 这里我们假设它完成了初始设置,并可能将任务传递给 CoderAgent(通过 Mediator 或直接)。\n log_agent_activity(self.name, \"Design request submitted. Forwarding to CoderAgent.\")\n # 实际中,这里可能没有返回值,而是通过 Mediator 驱动后续流程。\n # 为了让 Gradio 的流程更明确,我们让 run_workflow_gradio 来编排。\n return \"UserProxy: Design request initiated.\"\n\n elif self.name == \"CoderAgent\":\n log_agent_activity(self.name,\n f\"Generating code based on: '{str(prompt_or_data)[:50]}...' using LLM {self.config.RAG_LLM_MODEL}\")\n time.sleep(0.5) # 模拟 LLM 调用\n generated_code = f\"// Generated by CoderAgent with {self.config.RAG_LLM_MODEL}\\n\" \\\n f\"module design_from_{self.name} (input clk, input rst_n);\\n\" \\\n f\" // Prompt: {str(prompt_or_data)[:30]}...\\nendmodule\"\n self.mediator.set_code(generated_code, agent=self.name)\n return generated_code\n\n elif self.name == \"Reviewer\":\n code_to_review = prompt_or_data # 假设 Reviewer 接收代码作为输入\n log_agent_activity(self.name,\n f\"Reviewing code (first 50 chars): '{str(code_to_review)[:50]}...' using LLM {self.config.RAG_LLM_MODEL}\")\n time.sleep(0.3)\n review_comment = f\"Reviewer {self.name}: Code looks acceptable (dummy review).\"\n log_agent_activity(self.name, review_comment)\n # Reviewer 可能更新 Mediator 中的代码或添加审查意见\n # self.mediator.add_review_comment(review_comment, agent=self.name)\n return review_comment # 或者返回被审查的代码/状态\n\n elif self.name == \"Executor\":\n # Executor 接收 Verilog 代码和 Testbench 路径\n # prompt_or_data 在这里应该是包含代码和 testbench_path 的字典或元组\n # 为了简单,我们假设它从 Mediator 获取代码,testbench_path 从 config_module 获取\n code_to_simulate = self.mediator.get_latest_code()\n testbench_path = self.config.TESTBENCH_PATH # 已在 setup_environment_dummy 中设置\n log_agent_activity(self.name,\n f\"Executing simulation for code with testbench: {os.path.basename(testbench_path)}\")\n time.sleep(0.5)\n\n current_experiment_path = os.path.dirname(testbench_path)\n exp_out_path = os.path.join(current_experiment_path, \"expected_output.txt\")\n expected_output_content = \"Default Expected: Time=0: in=0, out=1\\nTime=10: in=1, out=0\"\n if os.path.exists(exp_out_path):\n with open(exp_out_path, 'r', encoding='utf-8') as f:\n expected_output_content = f.read().strip()\n else:\n log_agent_activity(self.name, f\"Warning: Expected output file not found at {exp_out_path}\")\n\n sim_passed = \"simple_pass_condition\" in str(code_to_simulate).lower() # Dummy pass/fail\n actual_sim_output = expected_output_content if sim_passed else \"Executor Sim: Time=0: in=0, out=0\\nTime=10: in=1, out=1\"\n sim_result_msg = \"All tests passed (Simulated by Executor).\" if sim_passed else \"Simulation failed (Simulated by Executor).\"\n sim_data = {\"execution_result\": actual_sim_output, \"message\": sim_result_msg,\n \"expected_output_used\": expected_output_content}\n self.mediator.set_simulation_result(sim_data, agent=self.name)\n return sim_data\n\n elif self.name == \"Summarizer\":\n # Summarizer 可能从 Mediator 获取所有相关信息\n final_code = self.mediator.get_latest_code()\n sim_results = self.mediator.get_latest_simulation_result()\n # metrics_data = self.mediator.get_metrics() # 假设 Mediator 也存储指标\n\n log_agent_activity(self.name, f\"Summarizing results using LLM {self.config.RAG_LLM_MODEL}...\")\n time.sleep(0.2)\n summary = f\"Summary by {self.name} (LLM: {self.config.RAG_LLM_MODEL}):\\n\"\n summary += f\"- Code: {'Available' if final_code else 'N/A'}.\\n\"\n summary += f\"- Simulation: {sim_results.get('message', 'N/A') if sim_results else 'N/A'}\\n\"\n # summary += f\"- Metrics: {str(metrics_data)[:50]}...\\nWorkflow concluded.\"\n summary += \"Workflow concluded (dummy summary).\"\n self.mediator.set_summary(summary, agent=self.name)\n log_agent_activity(self.name, \"Summary generated.\")\n return summary\n else:\n log_agent_activity(self.name, \"Unknown task or role.\")\n return \"Agent action not defined for this role in dummy.\"\n\n\n# --- setup_environment_dummy, initialize_rag_system_dummy, analyze_verilog_dummy (保持不变) ---\ndef setup_environment_dummy(experiment_path, testbench_src_path, expected_out_src_path, prompt_content):\n logging.info(f\"Setting up dummy environment in: {experiment_path}\")\n os.makedirs(experiment_path, exist_ok=True)\n verilog_dir = os.path.join(experiment_path, \"generated_verilog\");\n os.makedirs(verilog_dir, exist_ok=True)\n config_module.VERILOG_DIR = verilog_dir\n testbench_dest_path = os.path.join(experiment_path, \"testbench_ref.v\")\n if testbench_src_path and os.path.exists(testbench_src_path):\n shutil.copy(testbench_src_path, testbench_dest_path)\n else:\n logging.warning(f\"Testbench source file not found at {testbench_src_path}, creating dummy.\");\n with open(testbench_dest_path, \"w\") as f:\n f.write(\"// Dummy testbench file\\n\")\n config_module.TESTBENCH_PATH = testbench_dest_path\n if expected_out_src_path and os.path.exists(expected_out_src_path):\n shutil.copy(expected_out_src_path, os.path.join(experiment_path, \"expected_output.txt\"))\n else:\n logging.warning(f\"Expected output source file not found at {expected_out_src_path}.\")\n with open(os.path.join(experiment_path, \"prompt.txt\"), \"w\", encoding=\"utf-8\") as f:\n f.write(prompt_content)\n config_module.SUMMARY_FILE = os.path.join(experiment_path, \"summary.json\")\n config_module.EXPERIMENT_LOG_FILE = os.path.join(experiment_path, \"experiment_run.log\")\n with open(config_module.EXPERIMENT_LOG_FILE, \"w\", encoding=\"utf-8\") as f:\n f.write(f\"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S,%f')[:-3]}] [INFO] [System] Log initialized.\\n\")\n return (verilog_dir, testbench_dest_path, None, config_module.SUMMARY_FILE, experiment_path)\n\n\ndef initialize_rag_system_dummy(*args_from_config): # 接收来自 config_module 的参数\n logging.info(\n f\"Initializing dummy RAG system with KNOWLEDGE_BASE_PATH: {args_from_config[0]}, RAG_LLM_MODEL: {args_from_config[3]}\")\n return \"rag_system_instance_dummy\"\n\n\ndef analyze_verilog_dummy(verilog_code, module_name, model=\"QWQ\"): # (保持不变)\n logging.info(f\"Analyzing Verilog (dummy) for module: {module_name}\");\n if not verilog_code or not module_name: return {\"error\": \"No code/module for analysis.\"}\n return {\"logic_gate_count\": 10, \"longest_delay\": 5.0, \"longest_path\": [\"A\", \"B\", \"C\"]}\n\n\n# --- GradioAppState, log_agent_activity (保持不变) ---\nclass GradioAppState:\n def __init__(self):\n self.current_log_file_path = None;\n self.current_log_last_pos = 0\n self.log_processor = LogProcessor(\n r'^\\[(?P<timestamp>\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3})\\] \\[(?P<log_level>.*?)\\] \\[(?P<agent_name>.*?)\\]\\s*(?P<message>.*)',\n r'^\\s*, agent=(?P<agent_name>[^\\s,]+)(?:, message=(?P<message>.*))?$', '%Y-%m-%d %H:%M:%S,%f')\n self.agent_interaction_records = [];\n self.testbench_file_path = None;\n self.expected_output_file_path = None\n self.active_model_name = config_module.default_model;\n self.active_api_key = config_module.get_current_api_key_value(config_module.default_model)\n\n\napp_state = GradioAppState()\n\n\ndef log_agent_activity(agent_name, message):\n log_file_to_use = app_state.current_log_file_path\n if log_file_to_use and os.path.exists(os.path.dirname(log_file_to_use)):\n timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S,%f')[:-3]\n log_entry = f\"[{timestamp}] [DIALOGUE] [{agent_name}] {message}\\n\"\n try:\n with open(log_file_to_use, \"a\", encoding=\"utf-8\") as f:\n f.write(log_entry)\n except Exception as e:\n logging.error(f\"Failed to write log to {log_file_to_use}: {e}\")\n else:\n logging.info(f\"Log (no file: {log_file_to_use}): [{agent_name}] {message}\")\n\n\n# --- Gradio UI Definition (保持不变) ---\nwith gr.Blocks(theme=gr.themes.Soft(), title=\"VeriOptima (Gradio)\") as demo:\n gr.Markdown(\"# VeriOptima: Verilog Optimization & Chat Agent\")\n with gr.Row(equal_height=False): # Global selectors\n shared_model_selector = gr.Dropdown(label=\"Select LLM Model (Global)\",\n choices=list(config_module.api_configs.keys()),\n value=app_state.active_model_name, interactive=True)\n shared_api_key_input = gr.Textbox(label=\"API Key (Global, overrides if set)\",\n placeholder=\"Enter API key for selected model\",\n value=app_state.active_api_key, type=\"password\", interactive=True)\n gr.HTML(\"<hr>\")\n with gr.Tabs(selected=0) as tabs:\n with gr.TabItem(\"CircuitMind (Design Optimization)\", id=0):\n with gr.Row():\n with gr.Column(scale=2): # Inputs\n design_prompt_input = gr.Textbox(lines=5, label=\"Design Requirements / Verilog to Optimize\",\n placeholder=\"Enter design prompt or Verilog code...\")\n with gr.Row(): design_testbench_file = gr.File(\n label=\"Upload Testbench File (.v)\"); design_expected_output_file = gr.File(\n label=\"Upload Expected Output File (.txt)\")\n with gr.Row(): design_testbench_preview = gr.Textbox(label=\"Testbench Preview\", lines=8,\n interactive=False,\n max_lines=10); design_expected_output_preview = gr.Textbox(\n label=\"Expected Output Preview\", lines=8, interactive=False, max_lines=10)\n design_run_button = gr.Button(\"Run Optimization Workflow\", variant=\"primary\")\n with gr.Column(scale=1): # Outputs\n generated_verilog_display = gr.Code(label=\"Generated/Optimized Verilog Code\", language=None,\n interactive=False) # language=None for plaintext\n gate_metrics_display = gr.Textbox(label=\"Gate Metrics & Analysis\", lines=5, interactive=False)\n workflow_status_display = gr.Textbox(label=\"Workflow Status & Main Results\", lines=5,\n interactive=False)\n gr.Markdown(\"### Agent Interactions Log\");\n agent_log_display_html = gr.HTML(value=\"<p>Agent interactions will appear here...</p>\")\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: \"<p>Workflow initializing...</p>\",\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=(?P<agent_name>[^\\s,]+)(?:, message=(?P<message>.*))?$'\n # This seems intended for a very specific format, might not be common.\n # For simplicity in Gradio, we primarily rely on the main log_format.\n # If agent_format is critical, its handling needs to be integrated here.\n\n self.flush_buffer(chat_records) # Flush any remaining messages\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:\n return \"<p>No agent interactions logged yet.</p>\"\n\n html_parts = [\n \"<style>\",\n \"body { font-family: Arial, sans-serif; margin: 0px; }\",\n \".message-container { margin-bottom: 12px; display: flex; align-items: flex-start; padding: 5px;}\",\n \".avatar-container { width: 32px; height: 32px; margin-right: 8px; flex-shrink: 0; display:flex; align-items:center; justify-content:center; }\",\n \".avatar-img { width: 30px; height: 30px; border-radius: 50%; }\",\n \".avatar-emoji { font-size: 24px; }\",\n \".content-container { display: flex; flex-direction: column; max-width: calc(100% - 50px); }\",\n \".header { display: flex; align-items: center; margin-bottom: 3px; }\",\n \".agent-name { font-weight: bold; margin-right: 8px; font-size: 0.9em; color: #333; }\",\n \".timestamp { color: #777; font-size: 0.8em; }\",\n \".bubble { background-color: #f0f0f0; border-radius: 8px; padding: 8px 10px; font-size: 0.95em; color: #333; white-space: pre-wrap; word-wrap: break-word; box-shadow: 0 1px 1px rgba(0,0,0,0.05); }\",\n \"</style>\",\n \"<div>\" # Main container for all messages\n ]\n\n for record in chat_records_list:\n escaped_message = html.escape(record['message'])\n agent_name = html.escape(record['agent'])\n timestamp = html.escape(record['time'])\n avatar_data = record.get('avatar', {})\n\n html_parts.append('<div class=\"message-container\">')\n html_parts.append('<div class=\"avatar-container\">')\n if avatar_data.get('type') == 'image' and avatar_data.get('value') and os.path.exists(avatar_data['value']):\n # Serve icons by placing 'icons' directory in the same place as this script,\n # or use absolute paths if necessary and security allows.\n # For Gradio, it's often best if static files are served by Gradio or a web server.\n # If `avatar_data['value']` is like \"icons/user.png\", it should work if 'icons' is in cwd.\n # Gradio can serve files if an `allowed_paths` is set or using `gr.File(value=\"path/to/img.png\")`\n # For now, assume the path is resolvable by the browser if Gradio runs locally.\n # A more robust way is to copy icons to a known served path or use base64, but that adds complexity.\n avatar_path_web = avatar_data['value'] # Pass through; ensure this path is web accessible\n html_parts.append(f'<img src=\"{avatar_path_web}\" class=\"avatar-img\" alt=\"{agent_name}\">')\n else:\n emoji_val = avatar_data.get('value', AGENT_AVATARS[\"default\"][\"emoji\"])\n html_parts.append(f'<span class.avatar-emoji\">{emoji_val}</span>')\n html_parts.append('</div>') # end avatar-container\n\n html_parts.append('<div class=\"content-container\">')\n html_parts.append('<div class=\"header\">')\n html_parts.append(f'<span class=\"agent-name\">{agent_name}</span>')\n html_parts.append(f'<span class=\"timestamp\">[{timestamp}]</span>')\n html_parts.append('</div>') # end header\n html_parts.append(f'<div class=\"bubble\">{escaped_message}</div>')\n html_parts.append('</div>') # end content-container\n html_parts.append('</div>') # end message-container\n\n html_parts.append(\"</div>\")\n return \"\".join(html_parts)\n\n\ndef compare_simulation_results(actual_output, expected_output):\n actual_lines = actual_output.strip().split('\\n')\n actual_results = []\n for line in actual_lines:\n match = re.match(r\"Time=\\d+:\\s*in=(\\d),\\s*out=(\\d)\", line.strip())\n if match:\n actual_results.append((match.group(1), match.group(2)))\n\n expected_lines = expected_output.strip().split('\\n')\n expected_results = []\n for line in expected_lines:\n match = re.match(r\"Time=\\d+:\\s*in=(\\d),\\s*out=(\\d)\", line.strip())\n if match:\n expected_results.append((match.group(1), match.group(2)))\n\n if not actual_results and not expected_results and actual_output.strip() == expected_output.strip():\n return True, \"Outputs match (non-structured).\"\n if not actual_results or not expected_results:\n return False, f\"Output format mismatch or empty. Actual: {len(actual_results)} structured lines, Expected: {len(expected_results)} structured lines.\"\n\n if len(actual_results) != len(expected_results):\n return False, f\"Output length mismatch: expected {len(expected_results)} lines, got {len(actual_results)}\"\n\n for i, (actual, expected) in enumerate(zip(actual_results, expected_results)):\n if actual != expected:\n return False, f\"Mismatch at line {i + 1}: expected in={expected[0]}, out={expected[1]}; got in={actual[0]}, out={actual[1]}\"\n\n return True, \"All structured tests passed.\"\n\n\ndef extract_module_name(verilog_code):\n if not verilog_code: return None\n match = re.search(r'module\\s+(\\w+)\\s*\\(', verilog_code)\n return match.group(1) if match else None\n\n\n# --- Mock/Placeholder Backend Modules & Classes (replace with your actual imports) ---\n# These are simplified versions based on the PyQt5 WorkflowWorker's imports\nclass MockConfigModule:\n EXPERIMENTS_ROOT = \"./experiments_gradio\" # Ensure this directory exists or is created\n OPENAI_API_KEY = \"\" # Will be set from UI\n # Define other paths as expected by your backend logic\n KNOWLEDGE_BASE_PATH = './knowledge_base/RAG-data'\n DETAIL_KNOWLEDGE_BASE_PATH = './knowledge_base/RAG-data-detail'\n EMBEDDING_MODEL = 'nomic-embed-text'\n RAG_LLM_MODEL = 'qwen2.5-coder:14b' # Default, can be changed by user\n RAG_INDEX_PATH = './knowledge_base/RAG-data/vector_index.faiss'\n RAG_DATA_PATH = './knowledge_base/RAG-data/vector_data.json'\n SUMMARY_JSON_PATH_TEMPLATE = \"./knowledge_base/Model-incrementment/{model_name}.json\"\n\n # These will be dynamically set during workflow\n VERILOG_DIR = \"\"\n TESTBENCH_PATH = \"\"\n REFERENCE_CODE_PATH = None\n SUMMARY_FILE = \"\"\n EXPERIMENT_LOG_FILE = \"\" # Path to the current experiment's log file\n\n # Model configs from PyQt version\n api_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\": [\n \"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\"],\n \"BASE_URL\": \"https://api.openai.com/v1\",\n },\n \"gpt-4o-mini\": {\n \"API_KEY\": [\n \"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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\",\n \"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 \"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 default_model = \"qwen2.5-coder:14b\"\n\n def __init__(self):\n os.makedirs(self.EXPERIMENTS_ROOT, exist_ok=True)\n # Create knowledge base dirs if they don't exist to prevent errors in dummy setup\n os.makedirs(os.path.dirname(self.SUMMARY_JSON_PATH_TEMPLATE), exist_ok=True)\n os.makedirs(self.KNOWLEDGE_BASE_PATH, exist_ok=True)\n\n def get_current_api_key(self, model_name_str):\n return self.api_configs.get(model_name_str, {}).get(\"api_key\", \"\")\n\n def update_llm_settings(self, model_name, api_key_override):\n self.RAG_LLM_MODEL = model_name # Or whatever config field your backend uses for the main LLM\n if api_key_override:\n self.OPENAI_API_KEY = api_key_override\n else:\n self.OPENAI_API_KEY = self.get_current_api_key(model_name)\n logging.info(\n f\"Config updated: Model={model_name}, API Key Used: {'Override' if api_key_override else 'Default/Configured'}\")\n\n\nconfig_module = MockConfigModule()\n\n\n# --- Dummy backend functions/classes (replace with your actual logic) ---\ndef setup_environment_dummy(experiment_path, testbench_src_path, expected_out_src_path, prompt_content):\n logging.info(f\"Setting up dummy environment in: {experiment_path}\")\n os.makedirs(experiment_path, exist_ok=True)\n\n verilog_dir = os.path.join(experiment_path, \"generated_verilog\")\n os.makedirs(verilog_dir, exist_ok=True)\n config_module.VERILOG_DIR = verilog_dir\n\n # Copy testbench and expected output to experiment directory\n # The names \"testbench_ref.v\" and \"expected_output.txt\" are examples.\n # Your backend might expect specific names.\n testbench_dest_path = os.path.join(experiment_path, \"testbench_ref.v\")\n if testbench_src_path and os.path.exists(testbench_src_path):\n shutil.copy(testbench_src_path, testbench_dest_path)\n config_module.TESTBENCH_PATH = testbench_dest_path\n else:\n logging.warning(\"Testbench source path not provided or not found.\")\n # Create a dummy if not provided, so backend doesn't fail on missing file\n with open(testbench_dest_path, \"w\") as f:\n f.write(\"// Dummy testbench from setup\")\n config_module.TESTBENCH_PATH = testbench_dest_path\n\n if expected_out_src_path and os.path.exists(expected_out_src_path):\n shutil.copy(expected_out_src_path, os.path.join(experiment_path, \"expected_output.txt\")) # Example name\n\n # Write prompt to a file, as in original code\n prompt_file_path = os.path.join(experiment_path, \"prompt.txt\")\n with open(prompt_file_path, \"w\", encoding=\"utf-8\") as f:\n f.write(prompt_content)\n\n summary_file_path = os.path.join(experiment_path, \"summary.json\") # Example\n config_module.SUMMARY_FILE = summary_file_path\n\n experiment_log_file = os.path.join(experiment_path, \"experiment_run.log\")\n config_module.EXPERIMENT_LOG_FILE = experiment_log_file\n # Touch the log file\n with open(experiment_log_file, \"w\") as f:\n f.write(\n f\"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S,%f')[:-3]}] [INFO] [System] Log initialized for experiment.\\n\")\n\n # Return structure: verilog_dir, testbench_path_in_exp, ref_code_path, summary_file_path_in_exp, experiment_output_dir (can be experiment_path)\n return (verilog_dir, testbench_dest_path, None, summary_file_path, experiment_path)\n\n\ndef initialize_rag_system_dummy(*args):\n logging.info(\"Initializing dummy RAG system.\")\n return \"rag_system_instance\"\n\n\ndef analyze_verilog_dummy(verilog_code, module_name, model=\"QWQ\"): # model param from original\n logging.info(f\"Analyzing Verilog for module: {module_name} (dummy)\")\n if not verilog_code or not module_name:\n return {\"error\": \"No code or module name for analysis.\"}\n # Simulate some metrics\n num_lines = len(verilog_code.splitlines())\n return {\n \"logic_gate_count\": num_lines * 2 + 5, # Dummy calculation\n \"longest_delay\": round(num_lines * 0.3 + 1.2, 2),\n \"longest_path\": [f\"path_seg1_{module_name}\", \"internal_seg\", f\"out_seg_{num_lines}\"]\n }\n\n\nclass MediatorDummy:\n def __init__(self):\n self._latest_code = None\n self._latest_sim_result = None\n logging.info(\"MediatorDummy initialized.\")\n\n def get_latest_code(self, agent_name=\"\", message=\"\"): # Params from original\n return self._latest_code\n\n def get_latest_simulation_result(self, agent_name=\"\", message=\"\"):\n return self._latest_sim_result\n\n def set_code(self, code, agent=\"CoderAgent\"): # Helper for dummy\n self._latest_code = code\n log_agent_activity(agent, f\"Submitted code:\\n{code[:100]}...\")\n\n def set_simulation_result(self, result, agent=\"Executor\"): # Helper for dummy\n self._latest_sim_result = result\n log_agent_activity(agent, f\"Submitted simulation result: {result.get('message', 'No message')}\")\n\n\nclass AgentDummy:\n def __init__(self, name, mediator, config, rag_system=None):\n self.name = name\n self.mediator = mediator\n self.config = config # This should be our global config_module\n self.rag_system = rag_system\n logging.info(f\"AgentDummy '{name}' initialized.\")\n log_agent_activity(self.name, \"Initialized.\")\n\n def submit_design_request(self, prompt):\n log_agent_activity(self.name, f\"Received design request: '{prompt[:50]}...'\")\n if self.name == \"UserProxy\": # Simulate the workflow triggering other agents\n # Simulate CoderAgent\n log_agent_activity(\"CoderAgent\", \"Starting code generation process...\")\n time.sleep(0.5) # Simulate work\n generated_code = f\"module {extract_module_name(prompt) or 'top_module'} (\\n // ... inputs and outputs ...\\n);\\n // Verilog code based on: {prompt}\\n // Generated by CoderAgent\\nendmodule\"\n self.mediator.set_code(generated_code, agent=\"CoderAgent\")\n log_agent_activity(\"CoderAgent\", f\"Generated Verilog code. Size: {len(generated_code)} bytes.\")\n\n # Simulate Reviewer\n log_agent_activity(\"Reviewer\", \"Starting code review...\")\n time.sleep(0.3)\n log_agent_activity(\"Reviewer\", \"Code review: LGTM! (Looks Good To Me)\")\n\n # Simulate Executor\n log_agent_activity(\"Executor\", \"Preparing for simulation...\")\n time.sleep(0.5)\n # Try to read expected output from the environment if available\n expected_output_content = \"Time=0: in=0, out=1\\nTime=10: in=1, out=0\" # Default\n exp_out_path = os.path.join(os.path.dirname(self.config.TESTBENCH_PATH), \"expected_output.txt\")\n if os.path.exists(exp_out_path):\n with open(exp_out_path, 'r') as f:\n expected_output_content = f.read().strip()\n\n # Simulate a simple match or mismatch based on prompt\n sim_passed = \"simple\" in prompt.lower() # Dummy condition for pass/fail\n actual_sim_output = expected_output_content if sim_passed else \"Time=0: in=0, out=0\\nTime=10: in=1, out=1\"\n\n sim_result_msg = \"All tests passed.\" if sim_passed else \"Simulation failed: Output mismatch.\"\n self.mediator.set_simulation_result({\n \"execution_result\": actual_sim_output,\n \"message\": sim_result_msg\n }, agent=\"Executor\")\n log_agent_activity(\"Executor\", f\"Simulation finished. Result: {sim_result_msg}\")\n\n # Simulate Summarizer\n log_agent_activity(\"Summarizer\", \"Generating summary of the process...\")\n time.sleep(0.2)\n log_agent_activity(\"Summarizer\", \"Workflow concluded. Results available.\")\n\n\n# --- Gradio Application State and Helper ---\nclass GradioAppState:\n def __init__(self):\n self.current_log_file_path = None # Path for current workflow log\n self.current_log_last_pos = 0\n self.log_processor = LogProcessor(\n r'^\\[(?P<timestamp>\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3})\\] \\[(?P<log_level>.*?)\\] \\[(?P<agent_name>.*?)\\]\\s*(?P<message>.*)',\n r'^\\s*, agent=(?P<agent_name>[^\\s,]+)(?:, message=(?P<message>.*))?$', # Original agent_format regex\n '%Y-%m-%d %H:%M:%S,%f'\n )\n self.agent_interaction_records = [] # Store records for current display\n self.testbench_file_path = None # Temp path from gr.File\n self.expected_output_file_path = None # Temp path from gr.File\n self.active_model_name = config_module.default_model\n self.active_api_key = config_module.get_current_api_key(config_module.default_model)\n\n\napp_state = GradioAppState()\n\n\n# Helper to write to the designated experiment log file\ndef log_agent_activity(agent_name, message):\n if app_state.current_log_file_path and os.path.exists(os.path.dirname(app_state.current_log_file_path)):\n timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S,%f')[:-3]\n # Using a log level like DIALOGUE as in original parsing logic\n log_entry = f\"[{timestamp}] [DIALOGUE] [{agent_name}] {message}\\n\"\n try:\n with open(app_state.current_log_file_path, \"a\", encoding=\"utf-8\") as f:\n f.write(log_entry)\n except Exception as e:\n logging.error(f\"Failed to write agent activity to log {app_state.current_log_file_path}: {e}\")\n else:\n logging.info(f\"Log (no file): [{agent_name}] {message}\")\n\n\n# --- Gradio UI Definition ---\nwith gr.Blocks(theme=gr.themes.Soft(), title=\"VeriOptima (Gradio)\") as demo:\n gr.Markdown(\"# VeriOptima: Verilog Optimization & Chat Agent\")\n\n # Shared model and API key components (could be per-tab if desired)\n with gr.Row(equal_height=False):\n shared_model_selector = gr.Dropdown(\n label=\"Select LLM Model (Global)\",\n choices=list(config_module.api_configs.keys()),\n value=app_state.active_model_name,\n interactive=True\n )\n shared_api_key_input = gr.Textbox(\n label=\"API Key (Global, overrides default if set)\",\n placeholder=\"Enter API key for selected model\",\n value=app_state.active_api_key, # Show default/current\n type=\"password\",\n interactive=True\n )\n\n gr.HTML(\"<hr>\") # Visual separator\n\n with gr.Tabs() as tabs:\n with gr.TabItem(\"CircuitMind (Design Optimization)\", id=0):\n with gr.Row():\n with gr.Column(scale=2):\n design_prompt_input = gr.Textbox(lines=5, label=\"Design Requirements / Verilog to Optimize\",\n placeholder=\"Enter design prompt or paste Verilog code here...\")\n with gr.Row():\n design_testbench_file = gr.File(label=\"Upload Testbench File (.v)\")\n design_expected_output_file = gr.File(label=\"Upload Expected Output File (.txt)\")\n with gr.Row():\n design_testbench_preview = gr.Textbox(label=\"Testbench Preview\", lines=8, interactive=False,\n max_lines=10)\n design_expected_output_preview = gr.Textbox(label=\"Expected Output Preview\", lines=8,\n interactive=False, max_lines=10)\n design_run_button = gr.Button(\"Run Optimization Workflow\", variant=\"primary\")\n with gr.Column(scale=1):\n generated_verilog_display = gr.Code(label=\"Generated/Optimized Verilog Code\",\n interactive=False)\n gate_metrics_display = gr.Textbox(label=\"Gate Metrics & Analysis\", lines=5, interactive=False)\n workflow_status_display = gr.Textbox(label=\"Workflow Status & Main Results\", lines=5,\n interactive=False)\n\n gr.Markdown(\"### Agent Interactions Log\")\n agent_log_display_html = gr.HTML(value=\"<p>Agent interactions will appear here...</p>\")\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: \"<p>Workflow initializing...</p>\",\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)"}}}}