mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-06-29 15:31:05 +08:00
feat(go-agent): Ported retrieval node, added Keenable web search tool (#16396)
Ported retrieval node, added Keenable web search tool - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@@ -574,7 +574,7 @@ async def move_files(uid: str, src_file_ids: list, dest_file_id: str = None, new
|
||||
)
|
||||
except Exception as storage_err:
|
||||
raise RuntimeError(f"Move file failed at storage layer: {str(storage_err)}")
|
||||
if not moved:
|
||||
if moved is False:
|
||||
raise RuntimeError("Move file failed at storage layer")
|
||||
updates["parent_id"] = dest_folder_entry.id
|
||||
updates["location"] = new_location
|
||||
|
||||
@@ -178,6 +178,32 @@ def split_model_name(model_name: str):
|
||||
return pure_model_name, instance_name, provider_name
|
||||
|
||||
|
||||
def _resolve_instance_for_model(provider_obj, instance_name: str, model_name: str):
|
||||
instance_obj = TenantModelInstanceService.get_by_provider_id_and_instance_name(provider_obj.id, instance_name)
|
||||
if instance_obj:
|
||||
return instance_obj
|
||||
if instance_name != "default":
|
||||
raise LookupError(f"Instance {instance_name} not found for model {model_name}.")
|
||||
|
||||
active_instances = [
|
||||
inst for inst in TenantModelInstanceService.get_all_by_provider_id(provider_obj.id)
|
||||
if inst.status == ActiveStatusEnum.ACTIVE.value
|
||||
]
|
||||
if len(active_instances) == 1:
|
||||
logger.warning(
|
||||
"Model instance fallback applied for legacy default instance name",
|
||||
extra={
|
||||
"provider_name": provider_obj.provider_name,
|
||||
"requested_instance_name": instance_name,
|
||||
"resolved_instance_name": active_instances[0].instance_name,
|
||||
"model_name": model_name,
|
||||
},
|
||||
)
|
||||
return active_instances[0]
|
||||
|
||||
raise LookupError(f"Instance {instance_name} not found for model {model_name}.")
|
||||
|
||||
|
||||
def get_model_config_from_provider_instance(tenant_id, model_type: str|enum.Enum, model_name: str):
|
||||
pure_model_name, instance_name, provider_name = split_model_name(model_name)
|
||||
model_type_val = model_type if isinstance(model_type, str) else model_type.value
|
||||
@@ -203,9 +229,7 @@ def get_model_config_from_provider_instance(tenant_id, model_type: str|enum.Enum
|
||||
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_name)
|
||||
if not provider_obj:
|
||||
raise LookupError(f"Provider {provider_name} not found for model {model_name}.")
|
||||
instance_obj = TenantModelInstanceService.get_by_provider_id_and_instance_name(provider_obj.id, instance_name)
|
||||
if not instance_obj:
|
||||
raise LookupError(f"Instance {instance_name} not found for model {model_name}.")
|
||||
instance_obj = _resolve_instance_for_model(provider_obj, instance_name, model_name)
|
||||
model_obj = TenantModelService.get_by_provider_id_and_instance_id_and_model_type_and_model_name(provider_obj.id, instance_obj.id, model_type_val, pure_model_name)
|
||||
|
||||
api_key, is_tool, api_key_payload = _decode_api_key_config(instance_obj.api_key)
|
||||
@@ -242,7 +266,7 @@ def get_model_config_from_provider_instance(tenant_id, model_type: str|enum.Enum
|
||||
raise LookupError(f"Model provider config not found: {provider_name}")
|
||||
llm_list = [llm for llm in fac_list[0]["llm"] if llm["llm_name"] == pure_model_name]
|
||||
if not llm_list:
|
||||
raise LookupError(f"Model config not found: {model_name}")
|
||||
raise LookupError(f"Instance {instance_name} not found for model {model_name}.")
|
||||
llm_info = llm_list[0]
|
||||
if model_type_val not in _factory_model_types(llm_info):
|
||||
raise LookupError(f"Model {model_name} is not a {model_type_val} model.")
|
||||
@@ -268,9 +292,7 @@ def get_api_key(tenant_id: str, model_name: str):
|
||||
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_name)
|
||||
if not provider_obj:
|
||||
raise LookupError(f"Provider {provider_name} not found.")
|
||||
instance_obj = TenantModelInstanceService.get_by_provider_id_and_instance_name(provider_obj.id, instance_name)
|
||||
if not instance_obj:
|
||||
raise LookupError(f"Instance {instance_name} not found.")
|
||||
instance_obj = _resolve_instance_for_model(provider_obj, instance_name, model_name)
|
||||
return instance_obj.api_key
|
||||
|
||||
|
||||
@@ -279,9 +301,7 @@ def get_model_type_by_name(tenant_id: str, model_name: str):
|
||||
provider_obj = TenantModelProviderService.get_by_tenant_id_and_provider_name(tenant_id, provider_name)
|
||||
if not provider_obj:
|
||||
raise LookupError(f"Provider {provider_name} not found for model {model_name}.")
|
||||
instance_obj = TenantModelInstanceService.get_by_provider_id_and_instance_name(provider_obj.id, instance_name)
|
||||
if not instance_obj:
|
||||
raise LookupError(f"Instance {instance_name} not found for model {model_name}.")
|
||||
instance_obj = _resolve_instance_for_model(provider_obj, instance_name, model_name)
|
||||
model_objs = TenantModelService.get_by_provider_id_and_instance_id_and_model_name(provider_obj.id, instance_obj.id, pure_model_name)
|
||||
types_in_json = []
|
||||
if not model_objs:
|
||||
|
||||
@@ -279,16 +279,20 @@ def normalize_str(v: Any) -> Any:
|
||||
|
||||
def validate_uuid1_hex(v: Any) -> str:
|
||||
"""
|
||||
Validates and converts input to a UUID version 1 hexadecimal string.
|
||||
Validates and converts input to a UUID hexadecimal string.
|
||||
|
||||
This function performs strict validation and normalization:
|
||||
The function name is retained for backward compatibility; only UUID
|
||||
*format* is enforced (any version is accepted), because some IDs in the
|
||||
system originate from external imports and use non-v1 UUIDs.
|
||||
|
||||
This function performs validation and normalization:
|
||||
1. Accepts either UUID objects or UUID-formatted strings
|
||||
2. Verifies the UUID is version 1 (time-based)
|
||||
3. Returns the 32-character hexadecimal representation
|
||||
2. Returns the 32-character hexadecimal representation
|
||||
3. Rejects anything that is not a valid UUID
|
||||
|
||||
Args:
|
||||
v (Any): Input value to validate. Can be:
|
||||
- UUID object (must be version 1)
|
||||
- UUID object (any version)
|
||||
- String in UUID format (e.g. "550e8400-e29b-41d4-a716-446655440000")
|
||||
|
||||
Returns:
|
||||
@@ -296,9 +300,8 @@ def validate_uuid1_hex(v: Any) -> str:
|
||||
Example: "550e8400e29b41d4a716446655440000"
|
||||
|
||||
Raises:
|
||||
PydanticCustomError: With code "invalid_UUID1_format" when:
|
||||
PydanticCustomError: With code "invalid_uuid_format" when:
|
||||
- Input is not a UUID object or valid UUID string
|
||||
- UUID version is not 1
|
||||
- String doesn't match UUID format
|
||||
|
||||
Examples:
|
||||
@@ -311,20 +314,22 @@ def validate_uuid1_hex(v: Any) -> str:
|
||||
Invalid cases:
|
||||
>>> validate_uuid1_hex("not-a-uuid") # raises PydanticCustomError
|
||||
>>> validate_uuid1_hex(12345) # raises PydanticCustomError
|
||||
>>> validate_uuid1_hex(UUID(int=0)) # v4, raises PydanticCustomError
|
||||
|
||||
Notes:
|
||||
- Uses Python's built-in UUID parser for format validation
|
||||
- Version check prevents accidental use of other UUID versions
|
||||
- UUID version is no longer enforced (v1, v4, v7, etc. all accepted)
|
||||
- Hyphens in input strings are automatically removed in output
|
||||
"""
|
||||
try:
|
||||
uuid_obj = UUID(v) if isinstance(v, str) else v
|
||||
if uuid_obj.version != 1:
|
||||
raise PydanticCustomError("invalid_UUID1_format", "Must be a UUID1 format")
|
||||
if isinstance(v, UUID):
|
||||
uuid_obj = v
|
||||
elif isinstance(v, str):
|
||||
uuid_obj = UUID(v)
|
||||
else:
|
||||
raise TypeError
|
||||
return uuid_obj.hex
|
||||
except (AttributeError, ValueError, TypeError):
|
||||
raise PydanticCustomError("invalid_UUID1_format", "Invalid UUID1 format")
|
||||
raise PydanticCustomError("invalid_uuid_format", "Invalid UUID format")
|
||||
|
||||
|
||||
class Base(BaseModel):
|
||||
@@ -801,7 +806,7 @@ class DeleteReq(Base):
|
||||
|
||||
This post-processing validator performs:
|
||||
1. None input handling (pass-through)
|
||||
2. UUID version 1 validation for each list item
|
||||
2. UUID format validation for each list item (any version accepted)
|
||||
3. Duplicate value detection
|
||||
4. Returns normalized UUID hex strings or None
|
||||
|
||||
@@ -814,18 +819,18 @@ class DeleteReq(Base):
|
||||
- None if input was None
|
||||
- List of normalized UUID hex strings otherwise:
|
||||
* 32-character lowercase
|
||||
* Valid UUID version 1
|
||||
* Valid UUID format (any version)
|
||||
* Unique within list
|
||||
|
||||
Raises:
|
||||
PydanticCustomError: With structured error details when:
|
||||
- "invalid_UUID1_format": Any string fails UUIDv1 validation
|
||||
- "invalid_uuid_format": Any string fails UUID format validation
|
||||
- "duplicate_uuids": If duplicate IDs are detected
|
||||
|
||||
Validation Rules:
|
||||
- None input returns None
|
||||
- Empty list returns empty list
|
||||
- All non-None items must be valid UUIDv1
|
||||
- All non-None items must be valid UUIDs (any version)
|
||||
- No duplicates permitted
|
||||
- Original order preserved
|
||||
|
||||
@@ -840,12 +845,12 @@ class DeleteReq(Base):
|
||||
|
||||
Invalid cases:
|
||||
>>> validate_ids(["invalid"])
|
||||
# raises PydanticCustomError(invalid_UUID1_format)
|
||||
# raises PydanticCustomError(invalid_uuid_format)
|
||||
>>> validate_ids(["550e...", "550e..."])
|
||||
# raises PydanticCustomError(duplicate_uuids)
|
||||
|
||||
Security Notes:
|
||||
- Validates UUID version to prevent version spoofing
|
||||
- Validates UUID format (any version)
|
||||
- Duplicate check prevents data injection
|
||||
- None handling maintains pipeline integrity
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user