mirror of
https://github.com/NicolasBohn/NexQuant.git
synced 2026-07-27 15:37:44 +00:00
fix: support experimental support for Deepseek models and update docs about configuration (#1024)
* fix qlib running bug for deepseek and add configuration docs about deepseek
This commit is contained in:
@@ -153,7 +153,9 @@ Ensure the current user can run Docker commands **without using sudo**. You can
|
||||
|
||||
You can set your Chat Model and Embedding Model in the following ways:
|
||||
|
||||
- **Using LiteLLM (Default)**: We now support LiteLLM as a backend for integration with multiple LLM providers. You can configure in two ways:
|
||||
> **🔥 Attention**: We now provide experimental support for **DeepSeek** models! You can use DeepSeek's official API for cost-effective and high-performance inference. See the configuration example below for DeepSeek setup.
|
||||
|
||||
- **Using LiteLLM (Default)**: We now support LiteLLM as a backend for integration with multiple LLM providers. You can configure in multiple ways:
|
||||
|
||||
**Option 1: Unified API base for both models**
|
||||
```bash
|
||||
@@ -185,6 +187,22 @@ Ensure the current user can run Docker commands **without using sudo**. You can
|
||||
LITELLM_PROXY_API_BASE=https://api.siliconflow.cn/v1
|
||||
```
|
||||
|
||||
**Configuration Example: DeepSeek Setup**:
|
||||
|
||||
>Since many users encounter configuration errors when setting up DeepSeek. Here's a complete working example for DeepSeek Setup:
|
||||
```bash
|
||||
cat << EOF > .env
|
||||
# CHAT MODEL: Using DeepSeek Official API
|
||||
CHAT_MODEL=deepseek/deepseek-chat
|
||||
DEEPSEEK_API_KEY=<replace_with_your_deepseek_api_key>
|
||||
|
||||
# EMBEDDING MODEL: Using SiliconFlow for embedding since deepseek has no embedding model.
|
||||
# Note: embedding requires litellm_proxy prefix
|
||||
EMBEDDING_MODEL=litellm_proxy/BAAI/bge-m3
|
||||
LITELLM_PROXY_API_KEY=<replace_with_your_siliconflow_api_key>
|
||||
LITELLM_PROXY_API_BASE=https://api.siliconflow.cn/v1
|
||||
```
|
||||
|
||||
Notice: If you are using reasoning models that include thought processes in their responses (such as \<think> tags), you need to set the following environment variable:
|
||||
```bash
|
||||
REASONING_THINK_RM=True
|
||||
|
||||
@@ -16,6 +16,9 @@ Ensure the current user can run Docker commands **without using sudo**. You can
|
||||
LiteLLM Backend Configuration (Default)
|
||||
=======================================
|
||||
|
||||
.. note::
|
||||
🔥 **Attention**: We now provide experimental support for **DeepSeek** models! You can use DeepSeek's official API for cost-effective and high-performance inference. See the configuration example below for DeepSeek setup.
|
||||
|
||||
Option 1: Unified API base for both models
|
||||
------------------------------------------
|
||||
|
||||
@@ -48,6 +51,23 @@ Option 2: Separate API bases for Chat and Embedding models
|
||||
LITELLM_PROXY_API_KEY=<replace_with_your_siliconflow_api_key>
|
||||
LITELLM_PROXY_API_BASE=https://api.siliconflow.cn/v1
|
||||
|
||||
Configuration Example: DeepSeek Setup
|
||||
-------------------------------------
|
||||
|
||||
Many users encounter configuration errors when setting up DeepSeek. Here's a complete working example:
|
||||
|
||||
.. code-block:: Properties
|
||||
|
||||
# CHAT MODEL: Using DeepSeek Official API
|
||||
CHAT_MODEL=deepseek/deepseek-chat
|
||||
DEEPSEEK_API_KEY=<replace_with_your_deepseek_api_key>
|
||||
|
||||
# EMBEDDING MODEL: Using SiliconFlow for embedding since DeepSeek has no embedding model.
|
||||
# Note: embedding requires litellm_proxy prefix
|
||||
EMBEDDING_MODEL=litellm_proxy/BAAI/bge-m3
|
||||
LITELLM_PROXY_API_KEY=<replace_with_your_siliconflow_api_key>
|
||||
LITELLM_PROXY_API_BASE=https://api.siliconflow.cn/v1
|
||||
|
||||
Necessary parameters include:
|
||||
|
||||
- `CHAT_MODEL`: The model name of the chat model.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
from typing import Dict
|
||||
|
||||
from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback
|
||||
@@ -136,18 +137,28 @@ class FactorMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
|
||||
queried_similar_error_knowledge_to_render = queried_similar_error_knowledge_to_render[:-1]
|
||||
for _ in range(10):
|
||||
try:
|
||||
code = json.loads(
|
||||
APIBackend(
|
||||
use_chat_cache=FACTOR_COSTEER_SETTINGS.coder_use_cache
|
||||
).build_messages_and_create_chat_completion(
|
||||
user_prompt=user_prompt,
|
||||
system_prompt=system_prompt,
|
||||
json_mode=True,
|
||||
json_target_type=Dict[str, str],
|
||||
)
|
||||
)["code"]
|
||||
response = APIBackend(
|
||||
use_chat_cache=FACTOR_COSTEER_SETTINGS.coder_use_cache
|
||||
).build_messages_and_create_chat_completion(
|
||||
user_prompt=user_prompt,
|
||||
system_prompt=system_prompt,
|
||||
json_mode=True,
|
||||
json_target_type=Dict[str, str],
|
||||
)
|
||||
|
||||
try:
|
||||
code = json.loads(response)["code"]
|
||||
except json.decoder.JSONDecodeError:
|
||||
# extract python code block
|
||||
match = re.search(r"```python(.*?)```", response, re.DOTALL)
|
||||
if match:
|
||||
code = match.group(1).strip()
|
||||
else:
|
||||
raise # continue to retry
|
||||
|
||||
return code
|
||||
except json.decoder.JSONDecodeError:
|
||||
|
||||
except (json.decoder.JSONDecodeError, KeyError):
|
||||
pass
|
||||
else:
|
||||
return "" # return empty code if failed to get code after 10 attempts
|
||||
|
||||
+100
-7
@@ -1,15 +1,17 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import json
|
||||
import re
|
||||
import sqlite3
|
||||
import time
|
||||
import tokenize
|
||||
import uuid
|
||||
from abc import ABC, abstractmethod
|
||||
from copy import deepcopy
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional, cast
|
||||
from typing import Any, Callable, List, Optional, Tuple, cast
|
||||
|
||||
import pytz
|
||||
from pydantic import BaseModel, TypeAdapter
|
||||
@@ -31,6 +33,100 @@ except ImportError:
|
||||
openai_imported = False
|
||||
|
||||
|
||||
class JSONParser:
|
||||
"""JSON parser supporting multiple strategies"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.strategies: List[Callable[[str], str]] = [
|
||||
self._direct_parse,
|
||||
self._extract_from_code_block,
|
||||
self._fix_python_syntax,
|
||||
self._extract_with_fix_combined,
|
||||
]
|
||||
|
||||
def parse(self, content: str) -> str:
|
||||
"""Parse JSON content, automatically trying multiple strategies"""
|
||||
original_content = content
|
||||
|
||||
for strategy in self.strategies:
|
||||
try:
|
||||
return strategy(original_content)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
|
||||
# All strategies failed
|
||||
raise json.JSONDecodeError("Failed to parse JSON after all attempts", original_content, 0)
|
||||
|
||||
def _direct_parse(self, content: str) -> str:
|
||||
"""Strategy 1: Direct parsing (including handling extra data)"""
|
||||
try:
|
||||
json.loads(content)
|
||||
return content
|
||||
except json.JSONDecodeError as e:
|
||||
if "Extra data" in str(e):
|
||||
return self._extract_first_json(content)
|
||||
raise
|
||||
|
||||
def _extract_from_code_block(self, content: str) -> str:
|
||||
"""Strategy 2: Extract JSON from code block"""
|
||||
match = re.search(r"```json\s*(.*?)\s*```", content, re.DOTALL)
|
||||
if not match:
|
||||
raise json.JSONDecodeError("No JSON code block found", content, 0)
|
||||
|
||||
json_content = match.group(1).strip()
|
||||
return self._direct_parse(json_content)
|
||||
|
||||
def _fix_python_syntax(self, content: str) -> str:
|
||||
"""Strategy 3: Fix Python syntax before parsing"""
|
||||
fixed = self._fix_python_booleans(content)
|
||||
return self._direct_parse(fixed)
|
||||
|
||||
def _extract_with_fix_combined(self, content: str) -> str:
|
||||
"""Strategy 4: Combined strategy - fix Python syntax first, then extract the first JSON object"""
|
||||
fixed = self._fix_python_booleans(content)
|
||||
|
||||
# Try to extract code block from the fixed content
|
||||
match = re.search(r"```json\s*(.*?)\s*```", fixed, re.DOTALL)
|
||||
if match:
|
||||
fixed = match.group(1).strip()
|
||||
|
||||
return self._direct_parse(fixed)
|
||||
|
||||
@staticmethod
|
||||
def _fix_python_booleans(json_str: str) -> str:
|
||||
"""Safely fix Python-style booleans to JSON standard format using tokenize"""
|
||||
replacements = {"True": "true", "False": "false", "None": "null"}
|
||||
|
||||
try:
|
||||
out = []
|
||||
io_string = io.StringIO(json_str)
|
||||
tokens = tokenize.generate_tokens(io_string.readline)
|
||||
|
||||
for toknum, tokval, _, _, _ in tokens:
|
||||
if toknum == tokenize.NAME and tokval in replacements:
|
||||
out.append(replacements[tokval])
|
||||
else:
|
||||
out.append(tokval)
|
||||
|
||||
result = "".join(out)
|
||||
# Validate if the result is valid JSON
|
||||
json.loads(result)
|
||||
return result
|
||||
|
||||
except (tokenize.TokenError, json.JSONDecodeError):
|
||||
# If tokenize fails, fallback to regex method
|
||||
for python_val, json_val in replacements.items():
|
||||
json_str = re.sub(rf"\\b{python_val}\\b", json_val, json_str)
|
||||
return json_str
|
||||
|
||||
@staticmethod
|
||||
def _extract_first_json(response: str) -> str:
|
||||
"""Extract the first complete JSON object, ignoring extra content"""
|
||||
decoder = json.JSONDecoder()
|
||||
obj, _ = decoder.raw_decode(response)
|
||||
return json.dumps(obj)
|
||||
|
||||
|
||||
class SQliteLazyCache(SingletonBaseClass):
|
||||
def __init__(self, cache_location: str) -> None:
|
||||
super().__init__()
|
||||
@@ -482,12 +578,9 @@ class APIBackend(ABC):
|
||||
|
||||
# 3) format checking
|
||||
if json_mode:
|
||||
try:
|
||||
json.loads(all_response)
|
||||
except json.decoder.JSONDecodeError:
|
||||
match = re.search(r"```json(.*)```", all_response, re.DOTALL)
|
||||
all_response = match.groups()[0] if match else all_response
|
||||
json.loads(all_response)
|
||||
parser = JSONParser()
|
||||
all_response = parser.parse(all_response)
|
||||
|
||||
if json_target_type is not None:
|
||||
TypeAdapter(json_target_type).validate_json(all_response)
|
||||
if (response_format := kwargs.get("response_format")) is not None:
|
||||
|
||||
@@ -78,7 +78,7 @@ hypothesis_output_format_with_action: |-
|
||||
The output should follow JSON format. The schema is as follows:
|
||||
{
|
||||
"action": "If `hypothesis_specification` provides the action you need to take, please follow "hypothesis_specification" to choose the action. Otherwise, based on previous experimental results, suggest the action you believe is most appropriate at the moment. It should be one of [`factor`, `model`].",
|
||||
"hypothesis": "The new hypothesis generated based on the information provided.",
|
||||
"hypothesis": "The new hypothesis generated based on the information provided,should be a string.",
|
||||
"reason": "The reason why you generate this hypothesis. It should be comprehensive and logical. It should cover the other keys below and extend them. Limit in two or three sentences.",
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user