-
Notifications
You must be signed in to change notification settings - Fork 5
support auto llm review and fix test_api_connection #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
310f7ff
ab7d987
4258523
4020f69
93620ec
87c20be
addc2d8
34fc33f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -219,7 +219,7 @@ def apply_llm_config(current_llm_config, arg1, arg2, arg3, arg4, origin_call=Non | |||||||||||||
| data = { | ||||||||||||||
| "model": arg3, | ||||||||||||||
| "temperature": 0.01, | ||||||||||||||
| "messages": [{"role": "user", "content": "test"}], | ||||||||||||||
| "messages": [{"role": "user", "content": "hello"}], | ||||||||||||||
|
MrJs133 marked this conversation as resolved.
Comment on lines
219
to
+222
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changing the test message from 'test' to 'hello' is a minor change. Consider adding a comment explaining why this change was made or using a constant/configuration value instead of hardcoding the message. This would make the purpose of the change clearer and make it easier to update in the future.
Suggested change
|
||||||||||||||
| } | ||||||||||||||
| headers = {"Authorization": f"Bearer {arg1}"} | ||||||||||||||
| status_code = test_api_connection(test_url, method="POST", headers=headers, body=data, origin_call=origin_call) | ||||||||||||||
|
|
||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ | |
| # specific language governing permissions and limitations | ||
| # under the License. | ||
|
|
||
| import os | ||
| import asyncio | ||
| from contextlib import asynccontextmanager | ||
|
|
||
|
|
@@ -24,8 +25,10 @@ | |
| from fastapi import FastAPI | ||
|
|
||
| from hugegraph_llm.utils.hugegraph_utils import init_hg_test_data, run_gremlin_query, backup_data | ||
| from hugegraph_llm.utils.other_tool_utils import auto_test_llms | ||
| from hugegraph_llm.utils.log import log | ||
| from hugegraph_llm.demo.rag_demo.vector_graph_block import timely_update_vid_embedding | ||
| from hugegraph_llm.config import llm_settings, resource_path | ||
|
|
||
|
|
||
| def create_other_block(): | ||
|
|
@@ -42,14 +45,47 @@ def create_other_block(): | |
| out = gr.Textbox(label="Backup Graph Manually (Auto backup at 1:00 AM everyday)", show_copy_button=True) | ||
| btn = gr.Button("Backup Graph Data") | ||
| btn.click(fn=backup_data, inputs=inp, outputs=out) # pylint: disable=no-member | ||
| # auto test llm | ||
| with gr.Accordion("Evaluation Model Settings (only support openai)", open=True): | ||
| with gr.Row(): | ||
| review_model_name = gr.Textbox(label="Model Name", value="ernie-4.5-8k-preview", interactive=True) | ||
| review_max_tokens = gr.Textbox(label="Max Tokens", value=2048) | ||
| key = gr.Textbox(value=getattr(llm_settings, "openai_chat_api_key"), label="API Key") | ||
| base = gr.Textbox(value=getattr(llm_settings, "openai_chat_api_base"),label="API Base") | ||
| with gr.Row(): | ||
| with gr.Column(): | ||
| with gr.Tab("file") as tab_upload_file: # pylint: disable=W0612 | ||
| inp1_file = gr.File( | ||
| value=os.path.join(resource_path, "demo", "llm_review.yaml"), | ||
| label="yaml file", | ||
| file_count="single", | ||
| ) | ||
| with gr.Tab("text") as tab_upload_text: # pylint: disable=W0612 | ||
| inp1 = gr.Textbox( | ||
| value="openai, model_name, api_key, api_base, max_tokens\n" \ | ||
| "qianfan_wenxin, model_name, api_key, secret_key\n" \ | ||
| "ollama/local, model_name, host, port, max_tokens\n" \ | ||
| "litellm, model_name, api_key, api_base, max_tokens\n", | ||
| label="LLMs Config (every line represents a different LLM)", | ||
| show_copy_button=True, lines=6 | ||
| ) | ||
| with gr.Row(): | ||
| inp2 = gr.Textbox(value="hello, how are you?", label="Prompt", show_copy_button=True, lines=8) | ||
| inp3 = gr.Textbox(value="I am fine, thank you", label="Standard Answer", show_copy_button=True, lines=8) | ||
| out = gr.Code(label="Output", language="json", elem_classes="code-container-show") | ||
| btn = gr.Button("Run LLM Test") | ||
| btn.click( # pylint: disable=no-member | ||
| fn=auto_test_llms, | ||
| inputs=[inp1, inp1_file, inp2, inp3, review_model_name, review_max_tokens, key, base], | ||
| outputs=out | ||
| ) | ||
|
Comment on lines
+48
to
+81
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chain验证 Tab 切换功能是否完整 注意到您添加了 Tab 切换界面,但缺少响应 Tab 选择的回调函数。如果用户在两个 Tab 间切换,可能会导致数据不一致。建议验证这种情况下的行为是否符合预期。 🏁 Script executed: #!/bin/bash
# 检查之前是否存在 Tab 选择的实现或相关功能
grep -r "on_tab_select\|tab.*select\|select.*tab" --include="*.py" .Length of output: 565 补充 Tab 切换回调以保持数据一致性 在 hugegraph-llm/src/hugegraph_llm/demo/rag_demo/other_block.py (约48–81行)中新增了“file”/“text”两个 Tab,但未绑定切换回调。建议参考已实现的 vector_graph_block.py 中的 on_tab_select,用法示例:
请在 other_block.py 中对 🧰 Tools🪛 Ruff (0.11.9)49-50: Use a single (SIM117) 53-53: Do not call Replace (B009) 54-54: Do not call Replace (B009) 55-56: Use a single Combine (SIM117) 57-57: Local variable Remove assignment to unused variable (F841) 63-63: Local variable Remove assignment to unused variable (F841) 🤖 Prompt for AI Agents |
||
| with gr.Accordion("Init HugeGraph test data (🚧)", open=False): | ||
| with gr.Row(): | ||
| inp = [] | ||
| out = gr.Textbox(label="Init Graph Demo Result", show_copy_button=True) | ||
| btn = gr.Button("(BETA) Init HugeGraph test data (🚧)") | ||
| btn.click(fn=init_hg_test_data, inputs=inp, outputs=out) # pylint: disable=no-member | ||
|
|
||
|
|
||
| @asynccontextmanager | ||
| async def lifespan(app: FastAPI): # pylint: disable=W0621 | ||
| log.info("Starting background scheduler...") | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,11 @@ | ||||||||||||||
| - type: openai | ||||||||||||||
| model_name: ernie-4.5-8k-preview | ||||||||||||||
| api_key: | ||||||||||||||
| api_base: | ||||||||||||||
| max_tokens: 2048 | ||||||||||||||
|
Comment on lines
+3
to
+5
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The YAML file contains empty API keys and base URLs. Consider adding clear comments or documentation on how users should set these values or provide environment variable references instead of empty values.
Suggested change
|
||||||||||||||
|
|
||||||||||||||
| - type: openai | ||||||||||||||
| model_name: gpt-4.1-mini | ||||||||||||||
| api_key: | ||||||||||||||
| api_base: | ||||||||||||||
| max_tokens: 4096 | ||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,236 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Licensed to the Apache Software Foundation (ASF) under one | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # or more contributor license agreements. See the NOTICE file | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # distributed with this work for additional information | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # regarding copyright ownership. The ASF licenses this file | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # to you under the Apache License, Version 2.0 (the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # "License"); you may not use this file except in compliance | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # with the License. You may obtain a copy of the License at | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Unless required by applicable law or agreed to in writing, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # software distributed under the License is distributed on an | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # KIND, either express or implied. See the License for the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # specific language governing permissions and limitations | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # under the License. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import time | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import json | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import re | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import gradio as gr | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import yaml | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from hugegraph_llm.config import PromptConfig | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from hugegraph_llm.utils.log import log | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from hugegraph_llm.models.llms.ollama import OllamaClient | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from hugegraph_llm.models.llms.openai import OpenAIClient | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from hugegraph_llm.models.llms.qianfan import QianfanClient | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from hugegraph_llm.models.llms.litellm import LiteLLMClient | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def judge(answers, standard_answer, review_model_name, review_max_tokens, key, base): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MrJs133 marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| review_client = OpenAIClient( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| api_key=key, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| api_base=base, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model_name=review_model_name, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| max_tokens=int(review_max_tokens) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| review_prompt = PromptConfig.review_prompt.format(standard_answer=standard_answer) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for _, (model_name, answer) in enumerate(answers.items(), start=1): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| review_prompt += f"### {model_name}:\n{answer.strip()}\n\n" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.debug("Review_prompt: %s", review_prompt) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response = review_client.generate(prompt=review_prompt) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.debug("orig_review_response: %s", response) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| match = re.search(r'```json\n(.*?)\n```', response, re.DOTALL) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if match: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response = match.group(1).strip() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| reviews = json.loads(response) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return reviews | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
MrJs133 marked this conversation as resolved.
MrJs133 marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except Exception as e: # pylint: disable=W0718 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.error("Review failed: %s", str(e)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| reviews = {"error": f"Review error: {str(e)}"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return reviews | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def parse_llm_configurations(config_text: str): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| configs = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lines = config_text.strip().split("\n") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for i, line in enumerate(lines, 1): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fields = [x.strip() for x in line.split(",")] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not fields: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not fields: | |
| if not line.strip(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
跳过空行 / 注释行以避免误解析
line.split(",") 即使原行为空,也会得到长度为 1 的 [""],接下来会误进入各分支并抛 “字段数量不足” 异常。
- lines = config_text.strip().split("\n")
+ lines = [l for l in config_text.split("\n") if l.strip() and not l.strip().startswith("#")]这样能容忍用户在文本中添加空行或 # 开头的注释。
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def parse_llm_configurations(config_text: str): | |
| configs = [] | |
| lines = config_text.strip().split("\n") | |
| for i, line in enumerate(lines, 1): | |
| fields = [x.strip() for x in line.split(",")] | |
| if not fields: | |
| continue | |
| llm_type = fields[0] | |
| def parse_llm_configurations(config_text: str): | |
| configs = [] | |
| - lines = config_text.strip().split("\n") | |
| + lines = [l for l in config_text.split("\n") if l.strip() and not l.strip().startswith("#")] | |
| for i, line in enumerate(lines, 1): | |
| fields = [x.strip() for x in line.split(",")] | |
| if not fields: | |
| continue | |
| llm_type = fields[0] | |
| # … |
🤖 Prompt for AI Agents
In hugegraph-llm/src/hugegraph_llm/utils/other_tool_utils.py around lines 54 to
61, the current code does not properly skip empty lines or comment lines
starting with '#', causing incorrect parsing and potential "insufficient fields"
errors. Modify the loop to explicitly skip lines that are empty after stripping
or that start with '#' before splitting and processing them, ensuring these
lines do not cause parsing errors.
Copilot
AI
May 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LLM requests are made sequentially, which could increase total latency; consider running these calls concurrently (e.g., with asyncio or threads) to improve throughput.
| answers = {} | |
| for config in configs: | |
| output = None | |
| async def generate_output(config): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function is not validating that the returned reviews match the expected format before trying to transform them. This could lead to errors if the review model doesn't return data in the expected format.
| latency = time_end - time_start | |
| answers[config["model_name"]] = { | |
| "answer": output, | |
| "latency": f"{round(latency, 2)}s" | |
| } | |
| reviews = judge( | |
| {k: v["answer"] for k, v in answers.items()}, | |
| standard_answer, | |
| review_model_name, | |
| review_max_tokens, | |
| key, | |
| base | |
| ) | |
| log.debug("reviews: %s", reviews) | |
| result = {} | |
| reviews_dict = {item["model"]: item for item in reviews} if isinstance(reviews, list) else reviews | |
| for model_name, infos in answers.items(): | |
| result[model_name] = { | |
| "answer": infos["answer"], | |
| "latency": infos["latency"], | |
| "review": reviews_dict.get(model_name, {}) | |
| } | |
| return json.dumps(result, indent=4, ensure_ascii=False) if fmt else reviews | |
| reviews = judge( | |
| {k: v["answer"] for k, v in answers.items()}, | |
| standard_answer, | |
| review_model_name, | |
| review_max_tokens, | |
| key, | |
| base | |
| ) | |
| log.debug("reviews: %s", reviews) | |
| # Validate reviews format | |
| if isinstance(reviews, dict) and "error" in reviews: | |
| # Handle error case | |
| result = {} | |
| for model_name, infos in answers.items(): | |
| result[model_name] = { | |
| "answer": infos["answer"], | |
| "latency": infos["latency"], | |
| "review": {"error": reviews["error"]} | |
| } | |
| return json.dumps(result, indent=4, ensure_ascii=False) if fmt else reviews | |
| # Process valid reviews | |
| result = {} | |
| reviews_dict = {item["model"]: item for item in reviews} if isinstance(reviews, list) else reviews | |
| for model_name, infos in answers.items(): | |
| result[model_name] = { | |
| "answer": infos["answer"], | |
| "latency": infos["latency"], | |
| "review": reviews_dict.get(model_name, {}) | |
| } |
Uh oh!
There was an error while loading. Please reload this page.