mirror of
https://github.com/fawney19/Aether.git
synced 2026-01-02 15:52:26 +08:00
refactor: remove stream smoothing config from system settings and improve base image caching
- Remove stream_smoothing configuration from SystemConfigService (moved to handler default) - Remove stream smoothing UI controls from admin settings page - Add AdminClearSingleAffinityAdapter for targeted cache invalidation - Add clearSingleAffinity API endpoint to clear specific affinity cache entries - Include global_model_id in affinity list response for UI deletion support - Improve CI/CD workflow with hash-based base image change detection - Add hash label to base image for reliable cache invalidation detection - Use remote image inspection to determine if base image rebuild is needed - Include Dockerfile.base in hash calculation for proper dependency tracking
This commit is contained in:
37
.github/workflows/docker-publish.yml
vendored
37
.github/workflows/docker-publish.yml
vendored
@@ -15,6 +15,8 @@ env:
|
|||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
BASE_IMAGE_NAME: fawney19/aether-base
|
BASE_IMAGE_NAME: fawney19/aether-base
|
||||||
APP_IMAGE_NAME: fawney19/aether
|
APP_IMAGE_NAME: fawney19/aether
|
||||||
|
# Files that affect base image - used for hash calculation
|
||||||
|
BASE_FILES: "Dockerfile.base pyproject.toml frontend/package.json frontend/package-lock.json"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-base-changes:
|
check-base-changes:
|
||||||
@@ -23,8 +25,13 @@ jobs:
|
|||||||
base_changed: ${{ steps.check.outputs.base_changed }}
|
base_changed: ${{ steps.check.outputs.base_changed }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Log in to Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Check if base image needs rebuild
|
- name: Check if base image needs rebuild
|
||||||
id: check
|
id: check
|
||||||
@@ -34,10 +41,26 @@ jobs:
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if base-related files changed
|
# Calculate current hash of base-related files
|
||||||
if git diff --name-only HEAD~1 HEAD | grep -qE '^(Dockerfile\.base|pyproject\.toml|frontend/package.*\.json)$'; then
|
CURRENT_HASH=$(cat ${{ env.BASE_FILES }} 2>/dev/null | sha256sum | cut -d' ' -f1)
|
||||||
|
echo "Current base files hash: $CURRENT_HASH"
|
||||||
|
|
||||||
|
# Try to get hash label from remote image config
|
||||||
|
# Pull the image config and extract labels
|
||||||
|
REMOTE_HASH=""
|
||||||
|
if docker pull ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:latest 2>/dev/null; then
|
||||||
|
REMOTE_HASH=$(docker inspect ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:latest --format '{{ index .Config.Labels "org.opencontainers.image.base.hash" }}' 2>/dev/null) || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$REMOTE_HASH" ] || [ "$REMOTE_HASH" == "<no value>" ]; then
|
||||||
|
# No remote image or no hash label, need to rebuild
|
||||||
|
echo "No remote base image or hash label found, need rebuild"
|
||||||
|
echo "base_changed=true" >> $GITHUB_OUTPUT
|
||||||
|
elif [ "$CURRENT_HASH" != "$REMOTE_HASH" ]; then
|
||||||
|
echo "Hash mismatch: remote=$REMOTE_HASH, current=$CURRENT_HASH"
|
||||||
echo "base_changed=true" >> $GITHUB_OUTPUT
|
echo "base_changed=true" >> $GITHUB_OUTPUT
|
||||||
else
|
else
|
||||||
|
echo "Hash matches, no rebuild needed"
|
||||||
echo "base_changed=false" >> $GITHUB_OUTPUT
|
echo "base_changed=false" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -61,6 +84,12 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Calculate base files hash
|
||||||
|
id: hash
|
||||||
|
run: |
|
||||||
|
HASH=$(cat ${{ env.BASE_FILES }} 2>/dev/null | sha256sum | cut -d' ' -f1)
|
||||||
|
echo "hash=$HASH" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Extract metadata for base image
|
- name: Extract metadata for base image
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
@@ -69,6 +98,8 @@ jobs:
|
|||||||
tags: |
|
tags: |
|
||||||
type=raw,value=latest
|
type=raw,value=latest
|
||||||
type=sha,prefix=
|
type=sha,prefix=
|
||||||
|
labels: |
|
||||||
|
org.opencontainers.image.base.hash=${{ steps.hash.outputs.hash }}
|
||||||
|
|
||||||
- name: Build and push base image
|
- name: Build and push base image
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ export interface UserAffinity {
|
|||||||
key_name: string | null
|
key_name: string | null
|
||||||
key_prefix: string | null // Provider Key 脱敏显示(前4...后4)
|
key_prefix: string | null // Provider Key 脱敏显示(前4...后4)
|
||||||
rate_multiplier: number
|
rate_multiplier: number
|
||||||
|
global_model_id: string | null // 原始的 global_model_id(用于删除)
|
||||||
model_name: string | null // 模型名称(如 claude-haiku-4-5-20250514)
|
model_name: string | null // 模型名称(如 claude-haiku-4-5-20250514)
|
||||||
model_display_name: string | null // 模型显示名称(如 Claude Haiku 4.5)
|
model_display_name: string | null // 模型显示名称(如 Claude Haiku 4.5)
|
||||||
api_format: string | null // API 格式 (claude/openai)
|
api_format: string | null // API 格式 (claude/openai)
|
||||||
@@ -119,6 +120,18 @@ export const cacheApi = {
|
|||||||
await api.delete(`/api/admin/monitoring/cache/users/${userIdentifier}`)
|
await api.delete(`/api/admin/monitoring/cache/users/${userIdentifier}`)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除单条缓存亲和性
|
||||||
|
*
|
||||||
|
* @param affinityKey API Key ID
|
||||||
|
* @param endpointId Endpoint ID
|
||||||
|
* @param modelId GlobalModel ID
|
||||||
|
* @param apiFormat API 格式 (claude/openai)
|
||||||
|
*/
|
||||||
|
async clearSingleAffinity(affinityKey: string, endpointId: string, modelId: string, apiFormat: string): Promise<void> {
|
||||||
|
await api.delete(`/api/admin/monitoring/cache/affinity/${affinityKey}/${endpointId}/${modelId}/${apiFormat}`)
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清除所有缓存
|
* 清除所有缓存
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -142,32 +142,37 @@ async function resetAffinitySearch() {
|
|||||||
await fetchAffinityList()
|
await fetchAffinityList()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clearUserCache(identifier: string, displayName?: string) {
|
async function clearSingleAffinity(item: UserAffinity) {
|
||||||
const target = identifier?.trim()
|
const affinityKey = item.affinity_key?.trim()
|
||||||
if (!target) {
|
const endpointId = item.endpoint_id?.trim()
|
||||||
showError('无法识别标识符')
|
const modelId = item.global_model_id?.trim()
|
||||||
|
const apiFormat = item.api_format?.trim()
|
||||||
|
|
||||||
|
if (!affinityKey || !endpointId || !modelId || !apiFormat) {
|
||||||
|
showError('缓存记录信息不完整,无法删除')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const label = displayName || target
|
const label = item.user_api_key_name || affinityKey
|
||||||
|
const modelLabel = item.model_display_name || item.model_name || modelId
|
||||||
const confirmed = await showConfirm({
|
const confirmed = await showConfirm({
|
||||||
title: '确认清除',
|
title: '确认清除',
|
||||||
message: `确定要清除 ${label} 的缓存吗?`,
|
message: `确定要清除 ${label} 在模型 ${modelLabel} 上的缓存亲和性吗?`,
|
||||||
confirmText: '确认清除',
|
confirmText: '确认清除',
|
||||||
variant: 'destructive'
|
variant: 'destructive'
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!confirmed) return
|
if (!confirmed) return
|
||||||
|
|
||||||
clearingRowAffinityKey.value = target
|
clearingRowAffinityKey.value = affinityKey
|
||||||
try {
|
try {
|
||||||
await cacheApi.clearUserCache(target)
|
await cacheApi.clearSingleAffinity(affinityKey, endpointId, modelId, apiFormat)
|
||||||
showSuccess('清除成功')
|
showSuccess('清除成功')
|
||||||
await fetchCacheStats()
|
await fetchCacheStats()
|
||||||
await fetchAffinityList(tableKeyword.value.trim() || undefined)
|
await fetchAffinityList(tableKeyword.value.trim() || undefined)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showError('清除失败')
|
showError('清除失败')
|
||||||
log.error('清除用户缓存失败', error)
|
log.error('清除单条缓存失败', error)
|
||||||
} finally {
|
} finally {
|
||||||
clearingRowAffinityKey.value = null
|
clearingRowAffinityKey.value = null
|
||||||
}
|
}
|
||||||
@@ -618,7 +623,7 @@ onBeforeUnmount(() => {
|
|||||||
class="h-7 w-7 text-muted-foreground/70 hover:text-destructive"
|
class="h-7 w-7 text-muted-foreground/70 hover:text-destructive"
|
||||||
:disabled="clearingRowAffinityKey === item.affinity_key"
|
:disabled="clearingRowAffinityKey === item.affinity_key"
|
||||||
title="清除缓存"
|
title="清除缓存"
|
||||||
@click="clearUserCache(item.affinity_key, item.user_api_key_name || item.affinity_key)"
|
@click="clearSingleAffinity(item)"
|
||||||
>
|
>
|
||||||
<Trash2 class="h-3.5 w-3.5" />
|
<Trash2 class="h-3.5 w-3.5" />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -668,7 +673,7 @@ onBeforeUnmount(() => {
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="h-7 w-7 text-muted-foreground/70 hover:text-destructive shrink-0"
|
class="h-7 w-7 text-muted-foreground/70 hover:text-destructive shrink-0"
|
||||||
:disabled="clearingRowAffinityKey === item.affinity_key"
|
:disabled="clearingRowAffinityKey === item.affinity_key"
|
||||||
@click="clearUserCache(item.affinity_key, item.user_api_key_name || item.affinity_key)"
|
@click="clearSingleAffinity(item)"
|
||||||
>
|
>
|
||||||
<Trash2 class="h-3.5 w-3.5" />
|
<Trash2 class="h-3.5 w-3.5" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -465,77 +465,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</CardSection>
|
</CardSection>
|
||||||
|
|
||||||
<!-- 流式输出配置 -->
|
|
||||||
<CardSection
|
|
||||||
title="流式输出"
|
|
||||||
description="配置流式响应的输出效果"
|
|
||||||
>
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
||||||
<div class="md:col-span-2">
|
|
||||||
<div class="flex items-center space-x-2">
|
|
||||||
<Checkbox
|
|
||||||
id="stream-smoothing-enabled"
|
|
||||||
v-model:checked="systemConfig.stream_smoothing_enabled"
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<Label
|
|
||||||
for="stream-smoothing-enabled"
|
|
||||||
class="cursor-pointer"
|
|
||||||
>
|
|
||||||
启用平滑输出
|
|
||||||
</Label>
|
|
||||||
<p class="text-xs text-muted-foreground">
|
|
||||||
将上游返回的大块内容拆分成小块,模拟打字效果
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Label
|
|
||||||
for="stream-smoothing-chunk-size"
|
|
||||||
class="block text-sm font-medium"
|
|
||||||
>
|
|
||||||
每块字符数
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
id="stream-smoothing-chunk-size"
|
|
||||||
v-model.number="systemConfig.stream_smoothing_chunk_size"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
max="100"
|
|
||||||
placeholder="20"
|
|
||||||
class="mt-1"
|
|
||||||
:disabled="!systemConfig.stream_smoothing_enabled"
|
|
||||||
/>
|
|
||||||
<p class="mt-1 text-xs text-muted-foreground">
|
|
||||||
每次输出的字符数量(1-100)
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Label
|
|
||||||
for="stream-smoothing-delay-ms"
|
|
||||||
class="block text-sm font-medium"
|
|
||||||
>
|
|
||||||
输出间隔 (毫秒)
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
id="stream-smoothing-delay-ms"
|
|
||||||
v-model.number="systemConfig.stream_smoothing_delay_ms"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
max="100"
|
|
||||||
placeholder="8"
|
|
||||||
class="mt-1"
|
|
||||||
:disabled="!systemConfig.stream_smoothing_enabled"
|
|
||||||
/>
|
|
||||||
<p class="mt-1 text-xs text-muted-foreground">
|
|
||||||
每块之间的延迟毫秒数(1-100)
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardSection>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 导入配置对话框 -->
|
<!-- 导入配置对话框 -->
|
||||||
@@ -884,10 +813,6 @@ interface SystemConfig {
|
|||||||
log_retention_days: number
|
log_retention_days: number
|
||||||
cleanup_batch_size: number
|
cleanup_batch_size: number
|
||||||
audit_log_retention_days: number
|
audit_log_retention_days: number
|
||||||
// 流式输出
|
|
||||||
stream_smoothing_enabled: boolean
|
|
||||||
stream_smoothing_chunk_size: number
|
|
||||||
stream_smoothing_delay_ms: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
@@ -937,10 +862,6 @@ const systemConfig = ref<SystemConfig>({
|
|||||||
log_retention_days: 365,
|
log_retention_days: 365,
|
||||||
cleanup_batch_size: 1000,
|
cleanup_batch_size: 1000,
|
||||||
audit_log_retention_days: 30,
|
audit_log_retention_days: 30,
|
||||||
// 流式输出
|
|
||||||
stream_smoothing_enabled: false,
|
|
||||||
stream_smoothing_chunk_size: 20,
|
|
||||||
stream_smoothing_delay_ms: 8,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 计算属性:KB 和 字节 之间的转换
|
// 计算属性:KB 和 字节 之间的转换
|
||||||
@@ -997,10 +918,6 @@ async function loadSystemConfig() {
|
|||||||
'log_retention_days',
|
'log_retention_days',
|
||||||
'cleanup_batch_size',
|
'cleanup_batch_size',
|
||||||
'audit_log_retention_days',
|
'audit_log_retention_days',
|
||||||
// 流式输出
|
|
||||||
'stream_smoothing_enabled',
|
|
||||||
'stream_smoothing_chunk_size',
|
|
||||||
'stream_smoothing_delay_ms',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for (const key of configs) {
|
for (const key of configs) {
|
||||||
@@ -1108,22 +1025,6 @@ async function saveSystemConfig() {
|
|||||||
value: systemConfig.value.audit_log_retention_days,
|
value: systemConfig.value.audit_log_retention_days,
|
||||||
description: '审计日志保留天数'
|
description: '审计日志保留天数'
|
||||||
},
|
},
|
||||||
// 流式输出
|
|
||||||
{
|
|
||||||
key: 'stream_smoothing_enabled',
|
|
||||||
value: systemConfig.value.stream_smoothing_enabled,
|
|
||||||
description: '是否启用流式平滑输出'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'stream_smoothing_chunk_size',
|
|
||||||
value: systemConfig.value.stream_smoothing_chunk_size,
|
|
||||||
description: '流式平滑输出每个小块的字符数'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'stream_smoothing_delay_ms',
|
|
||||||
value: systemConfig.value.stream_smoothing_delay_ms,
|
|
||||||
description: '流式平滑输出每个小块之间的延迟毫秒数'
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const promises = configItems.map(item =>
|
const promises = configItems.map(item =>
|
||||||
|
|||||||
@@ -186,6 +186,30 @@ async def clear_user_cache(
|
|||||||
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/affinity/{affinity_key}/{endpoint_id}/{model_id}/{api_format}")
|
||||||
|
async def clear_single_affinity(
|
||||||
|
affinity_key: str,
|
||||||
|
endpoint_id: str,
|
||||||
|
model_id: str,
|
||||||
|
api_format: str,
|
||||||
|
request: Request,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
) -> Any:
|
||||||
|
"""
|
||||||
|
Clear a single cache affinity entry
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- affinity_key: API Key ID
|
||||||
|
- endpoint_id: Endpoint ID
|
||||||
|
- model_id: Model ID (GlobalModel ID)
|
||||||
|
- api_format: API format (claude/openai)
|
||||||
|
"""
|
||||||
|
adapter = AdminClearSingleAffinityAdapter(
|
||||||
|
affinity_key=affinity_key, endpoint_id=endpoint_id, model_id=model_id, api_format=api_format
|
||||||
|
)
|
||||||
|
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("")
|
@router.delete("")
|
||||||
async def clear_all_cache(
|
async def clear_all_cache(
|
||||||
request: Request,
|
request: Request,
|
||||||
@@ -655,6 +679,7 @@ class AdminListAffinitiesAdapter(AdminApiAdapter):
|
|||||||
"key_name": key.name if key else None,
|
"key_name": key.name if key else None,
|
||||||
"key_prefix": provider_key_masked,
|
"key_prefix": provider_key_masked,
|
||||||
"rate_multiplier": key.rate_multiplier if key else 1.0,
|
"rate_multiplier": key.rate_multiplier if key else 1.0,
|
||||||
|
"global_model_id": affinity.get("model_name"), # 原始的 global_model_id
|
||||||
"model_name": (
|
"model_name": (
|
||||||
global_model_map.get(affinity.get("model_name")).name
|
global_model_map.get(affinity.get("model_name")).name
|
||||||
if affinity.get("model_name") and global_model_map.get(affinity.get("model_name"))
|
if affinity.get("model_name") and global_model_map.get(affinity.get("model_name"))
|
||||||
@@ -817,6 +842,65 @@ class AdminClearUserCacheAdapter(AdminApiAdapter):
|
|||||||
raise HTTPException(status_code=500, detail=f"清除失败: {exc}")
|
raise HTTPException(status_code=500, detail=f"清除失败: {exc}")
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AdminClearSingleAffinityAdapter(AdminApiAdapter):
|
||||||
|
affinity_key: str
|
||||||
|
endpoint_id: str
|
||||||
|
model_id: str
|
||||||
|
api_format: str
|
||||||
|
|
||||||
|
async def handle(self, context: ApiRequestContext) -> Dict[str, Any]: # type: ignore[override]
|
||||||
|
db = context.db
|
||||||
|
try:
|
||||||
|
redis_client = get_redis_client_sync()
|
||||||
|
affinity_mgr = await get_affinity_manager(redis_client)
|
||||||
|
|
||||||
|
# 直接获取指定的亲和性记录(无需遍历全部)
|
||||||
|
existing_affinity = await affinity_mgr.get_affinity(
|
||||||
|
self.affinity_key, self.api_format, self.model_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if not existing_affinity:
|
||||||
|
raise HTTPException(status_code=404, detail="未找到指定的缓存亲和性记录")
|
||||||
|
|
||||||
|
# 验证 endpoint_id 是否匹配
|
||||||
|
if existing_affinity.endpoint_id != self.endpoint_id:
|
||||||
|
raise HTTPException(status_code=404, detail="未找到指定的缓存亲和性记录")
|
||||||
|
|
||||||
|
# 失效单条记录
|
||||||
|
await affinity_mgr.invalidate_affinity(
|
||||||
|
self.affinity_key, self.api_format, self.model_id, endpoint_id=self.endpoint_id
|
||||||
|
)
|
||||||
|
|
||||||
|
# 获取用于日志的信息
|
||||||
|
api_key = db.query(ApiKey).filter(ApiKey.id == self.affinity_key).first()
|
||||||
|
api_key_name = api_key.name if api_key else None
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"已清除单条缓存亲和性: affinity_key={self.affinity_key[:8]}..., "
|
||||||
|
f"endpoint_id={self.endpoint_id[:8]}..., model_id={self.model_id[:8]}..."
|
||||||
|
)
|
||||||
|
|
||||||
|
context.add_audit_metadata(
|
||||||
|
action="cache_clear_single",
|
||||||
|
affinity_key=self.affinity_key,
|
||||||
|
endpoint_id=self.endpoint_id,
|
||||||
|
model_id=self.model_id,
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"status": "ok",
|
||||||
|
"message": f"已清除缓存亲和性: {api_key_name or self.affinity_key[:8]}",
|
||||||
|
"affinity_key": self.affinity_key,
|
||||||
|
"endpoint_id": self.endpoint_id,
|
||||||
|
"model_id": self.model_id,
|
||||||
|
}
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as exc:
|
||||||
|
logger.exception(f"清除单条缓存亲和性失败: {exc}")
|
||||||
|
raise HTTPException(status_code=500, detail=f"清除失败: {exc}")
|
||||||
|
|
||||||
|
|
||||||
class AdminClearAllCacheAdapter(AdminApiAdapter):
|
class AdminClearAllCacheAdapter(AdminApiAdapter):
|
||||||
async def handle(self, context: ApiRequestContext) -> Dict[str, Any]: # type: ignore[override]
|
async def handle(self, context: ApiRequestContext) -> Dict[str, Any]: # type: ignore[override]
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -143,7 +143,6 @@ class Config:
|
|||||||
# STREAM_STATS_DELAY: 统计记录延迟(秒),等待流完全关闭
|
# STREAM_STATS_DELAY: 统计记录延迟(秒),等待流完全关闭
|
||||||
self.stream_prefetch_lines = int(os.getenv("STREAM_PREFETCH_LINES", "5"))
|
self.stream_prefetch_lines = int(os.getenv("STREAM_PREFETCH_LINES", "5"))
|
||||||
self.stream_stats_delay = float(os.getenv("STREAM_STATS_DELAY", "0.1"))
|
self.stream_stats_delay = float(os.getenv("STREAM_STATS_DELAY", "0.1"))
|
||||||
# 注:流式平滑输出配置已移至数据库系统设置(stream_smoothing_*)
|
|
||||||
|
|
||||||
# 验证连接池配置
|
# 验证连接池配置
|
||||||
self._validate_pool_config()
|
self._validate_pool_config()
|
||||||
|
|||||||
@@ -78,19 +78,6 @@ class SystemConfigService:
|
|||||||
"value": False,
|
"value": False,
|
||||||
"description": "是否自动删除过期的API Key(True=物理删除,False=仅禁用),仅管理员可配置",
|
"description": "是否自动删除过期的API Key(True=物理删除,False=仅禁用),仅管理员可配置",
|
||||||
},
|
},
|
||||||
# 流式平滑输出配置
|
|
||||||
"stream_smoothing_enabled": {
|
|
||||||
"value": False,
|
|
||||||
"description": "是否启用流式平滑输出,自动根据文本长度调整输出速度",
|
|
||||||
},
|
|
||||||
"stream_smoothing_chunk_size": {
|
|
||||||
"value": 20,
|
|
||||||
"description": "流式平滑输出每个小块的字符数",
|
|
||||||
},
|
|
||||||
"stream_smoothing_delay_ms": {
|
|
||||||
"value": 8,
|
|
||||||
"description": "流式平滑输出每个小块之间的延迟毫秒数",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
Reference in New Issue
Block a user