chore: format

This commit is contained in:
Timothy Jaeryang Baek committed 2026-06-01 13:56:55 -07:00
1 parent 1f49ebf431
commit 6fce92aa12
139 files changed
+92569 -3249

No files matched your search

+85 -56
View File
@@ -72,7 +72,6 @@ SPEECH_CACHE_DIR = CACHE_DIR / 'audio' / 'speech'
SPEECH_CACHE_DIR.mkdir(parents=True, exist_ok=True)
def is_audio_conversion_required(file_path):
"""
Check if the given audio file needs conversion to mp3.
@@ -307,8 +306,6 @@ async def update_audio_config(request: Request, form_data: AudioConfigUpdateForm
else:
request.app.state.faster_whisper_model = None
return {
'tts': {
'ENGINE': request.app.state.config.TTS_ENGINE,
@@ -377,7 +374,10 @@ async def _raise_tts_error(exc: Exception, r=None) -> None:
async def _write_tts_cache(
file_path: Path, audio: bytes, body_path: Path, payload: dict,
file_path: Path,
audio: bytes,
body_path: Path,
payload: dict,
) -> None:
"""Persist audio + request metadata to the speech cache."""
async with aiofiles.open(file_path, 'wb') as f:
@@ -405,16 +405,16 @@ async def _tts_openai(request, payload, file_path, file_body_path, user):
session = await get_session()
r = await session.post(
url=f'{request.app.state.config.TTS_OPENAI_API_BASE_URL}/audio/speech',
json=payload, headers=headers, ssl=AIOHTTP_CLIENT_SESSION_SSL,
json=payload,
headers=headers,
ssl=AIOHTTP_CLIENT_SESSION_SSL,
)
r.raise_for_status()
audio_data = await r.read()
content_type = r.headers.get('Content-Type', 'audio/mpeg')
if not await asyncio.to_thread(
transcode_audio_to_mp3, audio_data, content_type, file_path
):
if not await asyncio.to_thread(transcode_audio_to_mp3, audio_data, content_type, file_path):
async with aiofiles.open(file_path, 'wb') as f:
await f.write(audio_data)
@@ -595,9 +595,7 @@ async def speech(request: Request, user=Depends(get_verified_user)):
body = await request.body()
name = hashlib.sha256(
body
+ str(engine).encode('utf-8')
+ str(request.app.state.config.TTS_MODEL).encode('utf-8')
body + str(engine).encode('utf-8') + str(request.app.state.config.TTS_MODEL).encode('utf-8')
).hexdigest()
file_path = SPEECH_CACHE_DIR.joinpath(f'{name}.mp3')
@@ -628,8 +626,11 @@ async def _transcribe_whisper(request, file_path, languages, file_dir, id):
def _run():
segments, info = model.transcribe(
file_path, beam_size=5, vad_filter=WHISPER_VAD_FILTER,
language=languages[0], multilingual=WHISPER_MULTILINGUAL,
file_path,
beam_size=5,
vad_filter=WHISPER_VAD_FILTER,
language=languages[0],
multilingual=WHISPER_MULTILINGUAL,
)
log.info("Detected language '%s' with probability %f" % (info.language, info.language_probability))
return ''.join([segment.text for segment in list(segments)])
@@ -665,7 +666,9 @@ async def _transcribe_openai(request, file_path, filename, languages, file_dir,
r = await session.post(
url=f'{request.app.state.config.STT_OPENAI_API_BASE_URL}/audio/transcriptions',
headers=headers, data=form_data, ssl=AIOHTTP_CLIENT_SESSION_SSL,
headers=headers,
data=form_data,
ssl=AIOHTTP_CLIENT_SESSION_SSL,
)
if r.status == 200:
break
@@ -724,11 +727,7 @@ async def _transcribe_deepgram(request, file_path, languages, file_dir, id):
# Parse the Deepgram response structure
try:
transcript = (
body['results']['channels'][0]['alternatives'][0]
.get('transcript', '')
.strip()
)
transcript = body['results']['channels'][0]['alternatives'][0].get('transcript', '').strip()
except (KeyError, IndexError) as exc:
log.error(f'Malformed Deepgram response: {exc}')
raise Exception('Failed to parse Deepgram response') from exc
@@ -744,7 +743,11 @@ async def _transcribe_deepgram(request, file_path, languages, file_dir, id):
if r is not None:
try:
res = await r.json()
msg = res.get('error', {}).get('message', '') if isinstance(res.get('error'), dict) else str(res.get('error', ''))
msg = (
res.get('error', {}).get('message', '')
if isinstance(res.get('error'), dict)
else str(res.get('error', ''))
)
if msg:
detail = f'External: {msg}'
except Exception:
@@ -752,7 +755,6 @@ async def _transcribe_deepgram(request, file_path, languages, file_dir, id):
raise Exception(detail)
async def _transcribe_azure(request, file_path, filename, file_dir, id):
"""Transcribe audio via Azure Cognitive Services batch transcription."""
if not os.path.isfile(file_path):
@@ -762,7 +764,7 @@ async def _transcribe_azure(request, file_path, filename, file_dir, id):
if audio_size > AZURE_MAX_FILE_SIZE:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f'File size ({audio_size // (1024*1024)}MB) exceeds Azure limit of {AZURE_MAX_FILE_SIZE_MB}MB',
detail=f'File size ({audio_size // (1024 * 1024)}MB) exceeds Azure limit of {AZURE_MAX_FILE_SIZE_MB}MB',
)
api_key = request.app.state.config.AUDIO_STT_AZURE_API_KEY
@@ -773,11 +775,23 @@ async def _transcribe_azure(request, file_path, filename, file_dir, id):
# Default to a broad set of locales when none are configured
if len(locale_str) < 2:
locale_str = ','.join([
'en-US', 'es-ES', 'es-MX', 'fr-FR', 'hi-IN',
'it-IT', 'de-DE', 'en-GB', 'en-IN', 'ja-JP',
'ko-KR', 'pt-BR', 'zh-CN',
])
locale_str = ','.join(
[
'en-US',
'es-ES',
'es-MX',
'fr-FR',
'hi-IN',
'it-IT',
'de-DE',
'en-GB',
'en-IN',
'ja-JP',
'ko-KR',
'pt-BR',
'zh-CN',
]
)
if not api_key or not region:
raise HTTPException(status_code=400, detail='Azure API key and region are required for Azure STT')
@@ -785,7 +799,8 @@ async def _transcribe_azure(request, file_path, filename, file_dir, id):
# Build the transcription definition payload
definition = json.dumps(
{'locales': locale_str.split(','), 'diarization': {'maxSpeakers': max_speakers, 'enabled': True}}
if locale_str else {}
if locale_str
else {}
)
endpoint = (
base_url or f'https://{region}.api.cognitive.microsoft.com'
@@ -799,7 +814,8 @@ async def _transcribe_azure(request, file_path, filename, file_dir, id):
try:
session = await get_session()
r = await session.post(
url=endpoint, data=form_data,
url=endpoint,
data=form_data,
headers={'Ocp-Apim-Subscription-Key': api_key},
ssl=AIOHTTP_CLIENT_SESSION_SSL,
)
@@ -833,8 +849,10 @@ async def _transcribe_azure(request, file_path, filename, file_dir, id):
if 'code' in res and 'message' in res:
azure_code = res.get('innerError', {}).get('code', res['code'])
user_facing_codes = {
'EmptyAudioFile', 'AudioLengthLimitExceeded',
'NoLanguageIdentified', 'MultipleLanguagesIdentified',
'EmptyAudioFile',
'AudioLengthLimitExceeded',
'NoLanguageIdentified',
'MultipleLanguagesIdentified',
}
if azure_code in user_facing_codes:
detail = res['message']
@@ -851,7 +869,6 @@ async def _transcribe_azure(request, file_path, filename, file_dir, id):
)
async def transcription_handler(request, file_path, metadata, user=None):
filename = os.path.basename(file_path)
file_dir = os.path.dirname(file_path)
@@ -897,8 +914,7 @@ async def _transcribe_mistral(request, file_path, filename, metadata, file_dir,
try:
model = request.app.state.config.STT_MODEL or 'voxtral-mini-latest'
log.info(
f'Mistral STT - model: {model}, '
f'method: {"chat_completions" if use_chat_completions else "transcriptions"}'
f'Mistral STT - model: {model}, method: {"chat_completions" if use_chat_completions else "transcriptions"}'
)
session = await get_session()
@@ -932,17 +948,20 @@ async def _transcribe_mistral(request, file_path, filename, metadata, file_dir,
payload = {
'model': model,
'messages': [{
'role': 'user',
'content': [
{'type': 'input_audio', 'input_audio': audio_base64},
{'type': 'text', 'text': text_instruction},
],
}],
'messages': [
{
'role': 'user',
'content': [
{'type': 'input_audio', 'input_audio': audio_base64},
{'type': 'text', 'text': text_instruction},
],
}
],
}
r = await session.post(
url=f'{api_base_url}/chat/completions', json=payload,
url=f'{api_base_url}/chat/completions',
json=payload,
headers={'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'},
ssl=AIOHTTP_CLIENT_SESSION_SSL,
)
@@ -969,7 +988,8 @@ async def _transcribe_mistral(request, file_path, filename, metadata, file_dir,
form_data.add_field('file', open(file_path, 'rb'), filename=filename, content_type=mime_type)
r = await session.post(
url=f'{api_base_url}/audio/transcriptions', data=form_data,
url=f'{api_base_url}/audio/transcriptions',
data=form_data,
headers={'Authorization': f'Bearer {api_key}'},
ssl=AIOHTTP_CLIENT_SESSION_SSL,
)
@@ -1041,10 +1061,7 @@ async def transcribe(request: Request, file_path: str, metadata: Optional[dict]
results = []
try:
tasks = [
transcription_handler(request, chunk_path, metadata, user)
for chunk_path in chunk_paths
]
tasks = [transcription_handler(request, chunk_path, metadata, user) for chunk_path in chunk_paths]
for coro in asyncio.as_completed(tasks):
try:
results.append(await coro)
@@ -1226,7 +1243,8 @@ async def get_available_models(request: Request) -> list[dict]:
try:
async with session.get(
f'{base_url}/audio/models',
ssl=AIOHTTP_CLIENT_SESSION_SSL, timeout=_timeout,
ssl=AIOHTTP_CLIENT_SESSION_SSL,
timeout=_timeout,
) as resp:
resp.raise_for_status()
data = await resp.json()
@@ -1236,7 +1254,8 @@ async def get_available_models(request: Request) -> list[dict]:
try:
async with session.get(
f'{base_url}/models',
ssl=AIOHTTP_CLIENT_SESSION_SSL, timeout=_timeout,
ssl=AIOHTTP_CLIENT_SESSION_SSL,
timeout=_timeout,
) as resp:
resp.raise_for_status()
data = await resp.json()
@@ -1256,7 +1275,8 @@ async def get_available_models(request: Request) -> list[dict]:
'xi-api-key': request.app.state.config.TTS_API_KEY,
'Content-Type': 'application/json',
},
ssl=AIOHTTP_CLIENT_SESSION_SSL, timeout=_timeout,
ssl=AIOHTTP_CLIENT_SESSION_SSL,
timeout=_timeout,
) as resp:
resp.raise_for_status()
models = await resp.json()
@@ -1274,9 +1294,14 @@ async def get_available_models(request: Request) -> list[dict]:
async def get_models(request: Request, user=Depends(get_verified_user)):
return {'models': await get_available_models(request)}
_OPENAI_DEFAULT_VOICES = {
'alloy': 'alloy', 'echo': 'echo', 'fable': 'fable',
'onyx': 'onyx', 'nova': 'nova', 'shimmer': 'shimmer',
'alloy': 'alloy',
'echo': 'echo',
'fable': 'fable',
'onyx': 'onyx',
'nova': 'nova',
'shimmer': 'shimmer',
}
@@ -1292,7 +1317,8 @@ async def get_available_voices(request) -> dict:
session = await get_session()
async with session.get(
f'{base_url}/audio/voices',
ssl=AIOHTTP_CLIENT_SESSION_SSL, timeout=_timeout,
ssl=AIOHTTP_CLIENT_SESSION_SSL,
timeout=_timeout,
) as resp:
resp.raise_for_status()
data = await resp.json()
@@ -1311,7 +1337,8 @@ async def get_available_voices(request) -> dict:
'xi-api-key': request.app.state.config.TTS_API_KEY,
'Content-Type': 'application/json',
},
ssl=AIOHTTP_CLIENT_SESSION_SSL, timeout=_timeout,
ssl=AIOHTTP_CLIENT_SESSION_SSL,
timeout=_timeout,
) as resp:
resp.raise_for_status()
voices_data = await resp.json()
@@ -1330,7 +1357,8 @@ async def get_available_voices(request) -> dict:
async with session.get(
url,
headers={'Ocp-Apim-Subscription-Key': request.app.state.config.TTS_API_KEY},
ssl=AIOHTTP_CLIENT_SESSION_SSL, timeout=_timeout,
ssl=AIOHTTP_CLIENT_SESSION_SSL,
timeout=_timeout,
) as resp:
resp.raise_for_status()
voices = await resp.json()
@@ -1348,7 +1376,8 @@ async def get_available_voices(request) -> dict:
async with session.get(
f'{api_base_url}/audio/voices',
headers={'Authorization': f'Bearer {api_key}'},
ssl=AIOHTTP_CLIENT_SESSION_SSL, timeout=_timeout,
ssl=AIOHTTP_CLIENT_SESSION_SSL,
timeout=_timeout,
) as resp:
resp.raise_for_status()
voices_data = await resp.json()
+7 -3
View File
@@ -514,9 +514,13 @@ async def delete_all_user_chats(
@router.get('/list/user/{user_id}', response_model=list[ChatTitleIdResponse])
async def get_user_chat_list_by_user_id(
user_id: str, page: int | None = None, query: str | None = None,
order_by: str | None = None, direction: str | None = None,
user=Depends(get_admin_user), db: AsyncSession = Depends(get_async_session),
user_id: str,
page: int | None = None,
query: str | None = None,
order_by: str | None = None,
direction: str | None = None,
user=Depends(get_admin_user),
db: AsyncSession = Depends(get_async_session),
):
"""List chat summaries for a given user (admin-only endpoint)."""
if not ENABLE_ADMIN_CHAT_ACCESS:
+3 -10
View File
@@ -123,9 +123,7 @@ async def process_uploaded_file(
if _is_text_file(file_path):
content_type = 'text/plain'
stt_supported = getattr(
request.app.state.config, 'STT_SUPPORTED_CONTENT_TYPES', []
)
stt_supported = getattr(request.app.state.config, 'STT_SUPPORTED_CONTENT_TYPES', [])
if content_type and strict_match_mime_type(stt_supported, content_type):
# Audio / STT-supported files → transcribe then index
@@ -161,17 +159,12 @@ async def process_uploaded_file(
db=db_session,
)
else:
raise Exception(
f'File type {content_type} is not supported for processing'
)
raise Exception(f'File type {content_type} is not supported for processing')
else:
# Documents, or any file when an external engine is configured
if not content_type:
log.info(
f'File type {file.content_type} is not provided, '
'but trying to process anyway'
)
log.info(f'File type {file.content_type} is not provided, but trying to process anyway')
await process_file(
request,
ProcessFileForm(file_id=file_item.id),
+3 -16
View File
@@ -336,29 +336,16 @@ async def preview_group_access(
return {
'group': {'id': group.id, 'name': group.name},
'models': {
'items': [
{'id': m.id, 'name': m.name}
for m in active_models
if m.id in accessible_model_ids
],
'items': [{'id': m.id, 'name': m.name} for m in active_models if m.id in accessible_model_ids],
'total': len(active_models),
},
'knowledge': {
'items': [
{'id': k.id, 'name': k.name}
for k in all_knowledge
if k.id in accessible_knowledge_ids
],
'items': [{'id': k.id, 'name': k.name} for k in all_knowledge if k.id in accessible_knowledge_ids],
'total': len(all_knowledge),
},
'tools': {
'items': [
{'id': t.id, 'name': t.name}
for t in all_tools
if t.id in accessible_tool_ids
],
'items': [{'id': t.id, 'name': t.name} for t in all_tools if t.id in accessible_tool_ids],
'total': len(all_tools),
},
'permissions': group.permissions or {},
}
+6 -4
View File
@@ -447,6 +447,7 @@ def _is_same_origin(url: str, base_url: str) -> bool:
and comparing the three origin components eliminates those
attack vectors.
"""
def _default_port(scheme: str) -> int:
return 443 if scheme == 'https' else 80
@@ -455,8 +456,7 @@ def _is_same_origin(url: str, base_url: str) -> bool:
return (
parsed.scheme == trusted.scheme
and parsed.hostname == trusted.hostname
and (parsed.port or _default_port(parsed.scheme))
== (trusted.port or _default_port(trusted.scheme))
and (parsed.port or _default_port(parsed.scheme)) == (trusted.port or _default_port(trusted.scheme))
)
@@ -743,7 +743,8 @@ async def image_generations(
headers = {'Authorization': f'Bearer {request.app.state.config.COMFYUI_API_KEY}'}
image_data, content_type = await get_image_data(
image['url'], headers,
image['url'],
headers,
trusted_base_url=request.app.state.config.COMFYUI_BASE_URL,
)
_, url = await upload_image(
@@ -1102,7 +1103,8 @@ async def image_edits(
headers = {'Authorization': f'Bearer {request.app.state.config.IMAGES_EDIT_COMFYUI_API_KEY}'}
image_data, content_type = await get_image_data(
image_url, headers,
image_url,
headers,
trusted_base_url=request.app.state.config.IMAGES_EDIT_COMFYUI_BASE_URL,
)
_, url = await upload_image(
+5 -2
View File
@@ -1336,8 +1336,11 @@ async def add_files_to_knowledge_batch(
dir_map = {form.file_id: form.directory_id for form in new_entries}
for file_id in successful_file_ids:
await Knowledges.add_file_to_knowledge_by_id(
knowledge_id=id, file_id=file_id, user_id=user.id,
directory_id=dir_map.get(file_id), db=db,
knowledge_id=id,
file_id=file_id,
user_id=user.id,
directory_id=dir_map.get(file_id),
db=db,
)
# If there were any errors, include them in the response
+3 -1
View File
@@ -63,7 +63,9 @@ class MemoryUpdateModel(BaseModel):
@router.post('/add', response_model=MemoryModel | None)
async def add_memory(
request: Request, form_data: AddMemoryForm, user=Depends(get_verified_user),
request: Request,
form_data: AddMemoryForm,
user=Depends(get_verified_user),
):
"""Persist a new memory and embed it into the user's vector collection.
+19 -11
View File
@@ -220,8 +220,10 @@ async def get_model_tags(user=Depends(get_verified_user), db: AsyncSession = Dep
@router.post('/create', response_model=ModelModel | None)
async def create_new_model(
request: Request, form_data: ModelForm,
user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session),
request: Request,
form_data: ModelForm,
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
):
"""Create a new workspace model entry."""
if user.role != 'admin' and not await has_permission(
@@ -248,7 +250,8 @@ async def create_new_model(
else:
await _verify_knowledge_file_access(
getattr(form_data.meta, 'knowledge', None) if form_data.meta else None,
user, db,
user,
db,
)
form_data.access_grants = await filter_allowed_access_grants(
@@ -361,13 +364,14 @@ async def import_models(
try:
await _verify_knowledge_file_access(
(model_data.get('meta') or {}).get('knowledge'),
user, db,
user,
db,
)
except HTTPException:
log.warning(
'import_models: user %s skipped model %s '
'(knowledge file access denied)',
user.id, model_id,
'import_models: user %s skipped model %s (knowledge file access denied)',
user.id,
model_id,
)
continue
@@ -634,14 +638,17 @@ async def toggle_model_by_id(id: str, user=Depends(get_verified_user), db: Async
@router.post('/model/update', response_model=ModelModel | None)
async def update_model_by_id(
request: Request, form_data: ModelForm,
user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session),
request: Request,
form_data: ModelForm,
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
):
"""Update a workspace model's configuration."""
model = await Models.get_model_by_id(form_data.id, db=db)
if not model:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND,
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ERROR_MESSAGES.NOT_FOUND,
)
if (
@@ -662,7 +669,8 @@ async def update_model_by_id(
await _verify_knowledge_file_access(
getattr(form_data.meta, 'knowledge', None) if form_data.meta else None,
user, db,
user,
db,
)
form_data.access_grants = await filter_allowed_access_grants(
+19 -36
View File
@@ -338,9 +338,7 @@ async def get_all_models(request: Request, user: UserModel | None = None):
allowed_model_ids = api_config.get('model_ids', [])
if allowed_model_ids and 'models' in response:
response['models'] = [
m for m in response['models'] if m['model'] in allowed_model_ids
]
response['models'] = [m for m in response['models'] if m['model'] in allowed_model_ids]
for m in response.get('models', []):
if prefix_id:
@@ -350,11 +348,7 @@ async def get_all_models(request: Request, user: UserModel | None = None):
if connection_type:
m['connection_type'] = connection_type
models_dict = {
'models': merge_models_lists(
r.get('models', []) if r else None for r in responses
)
}
models_dict = {'models': merge_models_lists(r.get('models', []) if r else None for r in responses)}
# Annotate with expiry info from loaded-model state
try:
@@ -386,7 +380,8 @@ async def get_filtered_models(models, user, db=None):
db=db,
)
return [
m for m in models.get('models', [])
m
for m in models.get('models', [])
if (mi := model_infos.get(m['model'])) and (user.id == mi.user_id or mi.id in accessible_ids)
]
@@ -439,19 +434,13 @@ async def get_ollama_loaded_models(
for idx, response in enumerate(responses):
if not response:
continue
api_config = _resolve_api_config(
request.app.state.config, idx, request.app.state.config.OLLAMA_BASE_URLS[idx]
)
api_config = _resolve_api_config(request.app.state.config, idx, request.app.state.config.OLLAMA_BASE_URLS[idx])
prefix_id = api_config.get('prefix_id')
if prefix_id:
for m in response.get('models', []):
m['model'] = f'{prefix_id}.{m["model"]}'
return {
'models': merge_models_lists(
r.get('models', []) if r else None for r in responses
)
}
return {'models': merge_models_lists(r.get('models', []) if r else None for r in responses)}
@router.get('/api/version')
@@ -472,7 +461,8 @@ async def get_ollama_versions(
tasks = []
for idx, url in enumerate(request.app.state.config.OLLAMA_BASE_URLS):
api_config = request.app.state.config.OLLAMA_API_CONFIGS.get(
str(idx), request.app.state.config.OLLAMA_API_CONFIGS.get(url, {}),
str(idx),
request.app.state.config.OLLAMA_API_CONFIGS.get(url, {}),
)
if api_config.get('enable', True):
tasks.append(send_get_request(f'{url}/api/version', api_config.get('key')))
@@ -799,7 +789,8 @@ async def embed(
url = request.app.state.config.OLLAMA_BASE_URLS[url_idx]
api_config = request.app.state.config.OLLAMA_API_CONFIGS.get(
str(url_idx), request.app.state.config.OLLAMA_API_CONFIGS.get(url, {}),
str(url_idx),
request.app.state.config.OLLAMA_API_CONFIGS.get(url, {}),
)
key = get_api_key(url_idx, url, request.app.state.config.OLLAMA_API_CONFIGS)
@@ -852,7 +843,8 @@ async def embeddings(
url = request.app.state.config.OLLAMA_BASE_URLS[url_idx]
api_config = request.app.state.config.OLLAMA_API_CONFIGS.get(
str(url_idx), request.app.state.config.OLLAMA_API_CONFIGS.get(url, {}),
str(url_idx),
request.app.state.config.OLLAMA_API_CONFIGS.get(url, {}),
)
key = get_api_key(url_idx, url, request.app.state.config.OLLAMA_API_CONFIGS)
@@ -910,7 +902,8 @@ async def generate_completion(
url = request.app.state.config.OLLAMA_BASE_URLS[url_idx]
api_config = request.app.state.config.OLLAMA_API_CONFIGS.get(
str(url_idx), request.app.state.config.OLLAMA_API_CONFIGS.get(url, {}),
str(url_idx),
request.app.state.config.OLLAMA_API_CONFIGS.get(url, {}),
)
prefix_id = api_config.get('prefix_id')
@@ -1027,9 +1020,7 @@ async def generate_chat_completion(
if model_info is not None:
if model_info.base_model_id:
base_model_id = (
request.base_model_id if hasattr(request, 'base_model_id') else model_info.base_model_id
)
base_model_id = request.base_model_id if hasattr(request, 'base_model_id') else model_info.base_model_id
payload['model'] = base_model_id
params = model_info.params.model_dump()
@@ -1331,19 +1322,12 @@ async def get_openai_models(
raw_models = model_list.get('models', [])
now_ts = int(time.time())
models = [
{'id': m['model'], 'object': 'model', 'created': now_ts, 'owned_by': 'openai'}
for m in raw_models
]
models = [{'id': m['model'], 'object': 'model', 'created': now_ts, 'owned_by': 'openai'} for m in raw_models]
if user.role == 'user' and not BYPASS_MODEL_ACCESS_CONTROL:
model_ids = [m['id'] for m in models]
model_infos = {
mi.id: mi for mi in await Models.get_models_by_ids(model_ids, db=db)
}
user_group_ids = {
g.id for g in await Groups.get_groups_by_member_id(user.id, db=db)
}
model_infos = {mi.id: mi for mi in await Models.get_models_by_ids(model_ids, db=db)}
user_group_ids = {g.id for g in await Groups.get_groups_by_member_id(user.id, db=db)}
accessible_ids = await AccessGrants.get_accessible_resource_ids(
user_id=user.id,
resource_type='model',
@@ -1353,8 +1337,7 @@ async def get_openai_models(
db=db,
)
models = [
m for m in models
if (mi := model_infos.get(m['id'])) and (user.id == mi.user_id or mi.id in accessible_ids)
m for m in models if (mi := model_infos.get(m['id'])) and (user.id == mi.user_id or mi.id in accessible_ids)
]
return {'data': models, 'object': 'list'}
+5 -2
View File
@@ -239,8 +239,11 @@ async def get_prompt_by_id(
@router.post('/id/{prompt_id}/update', response_model=PromptModel | None)
async def update_prompt_by_id(
request: Request, prompt_id: str, form_data: PromptForm,
user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session),
request: Request,
prompt_id: str,
form_data: PromptForm,
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
):
"""Update a prompt's content, creating a new history entry if changed."""
prompt = await Prompts.get_prompt_by_id(prompt_id, db=db)
+1 -2
View File
@@ -1314,7 +1314,7 @@ def merge_docs_to_target_size(
prev = result[-1]
if not can_merge_chunks(prev, chunk):
return False
merged = f"{prev.page_content}\n\n{content}"
merged = f'{prev.page_content}\n\n{content}'
if measure(merged) > max_size:
return False
result[-1] = Document(page_content=merged, metadata={**prev.metadata})
@@ -2211,7 +2211,6 @@ async def search_web(request: Request, engine: str, query: str, user=None) -> li
raise Exception('No search engine API key found in environment variables')
@router.post('/process/web/search')
async def process_web_search(request: Request, form_data: SearchForm, user=Depends(get_verified_user)):
if not request.app.state.config.ENABLE_WEB_SEARCH:
+11 -5
View File
@@ -324,8 +324,10 @@ async def export_tools(
@router.post('/create', response_model=ToolResponse | None)
async def create_new_tools(
request: Request, form_data: ToolForm,
user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session),
request: Request,
form_data: ToolForm,
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
):
"""Create a new tool from user-supplied Python source code."""
if user.role != 'admin' and not (
@@ -448,14 +450,18 @@ async def get_tools_by_id(id: str, user=Depends(get_verified_user), db: AsyncSes
@router.post('/id/{id}/update', response_model=ToolModel | None)
async def update_tools_by_id(
request: Request, id: str, form_data: ToolForm,
user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session),
request: Request,
id: str,
form_data: ToolForm,
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
):
"""Update an existing tool's source code and metadata."""
tools = await Tools.get_tool_by_id(id, db=db)
if not tools:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND,
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ERROR_MESSAGES.NOT_FOUND,
)
# Is the user the original creator, in a group with write access, or an admin
+9 -17
View File
@@ -384,7 +384,9 @@ async def get_user_info_by_session_user(user=Depends(get_verified_user), db: Asy
@router.post('/user/info/update', response_model=dict | None)
async def update_user_info_by_session_user( # PATCH-style merge
form_data: dict, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session),
form_data: dict,
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
):
"""Merge caller-supplied fields into the current user's info dict.
@@ -401,6 +403,7 @@ async def update_user_info_by_session_user( # PATCH-style merge
)
return updated.info
############################
# GetUserById
############################
@@ -538,7 +541,8 @@ async def get_user_active_status_by_id(
@router.post('/{user_id}/update', response_model=UserModel | None)
async def update_user_by_id(
user_id: str, form_data: UserUpdateForm,
user_id: str,
form_data: UserUpdateForm,
session_user: UserModel = Depends(get_admin_user),
db: AsyncSession = Depends(get_async_session),
):
@@ -743,27 +747,15 @@ async def get_user_preview(
'user': {'id': target_user.id, 'name': target_user.name},
'groups': [{'id': g.id, 'name': g.name} for g in user_groups],
'models': {
'items': [
{'id': m.id, 'name': m.name}
for m in active_models
if m.id in accessible_model_ids
],
'items': [{'id': m.id, 'name': m.name} for m in active_models if m.id in accessible_model_ids],
'total': len(active_models),
},
'knowledge': {
'items': [
{'id': k.id, 'name': k.name}
for k in all_knowledge
if k.id in accessible_knowledge_ids
],
'items': [{'id': k.id, 'name': k.name} for k in all_knowledge if k.id in accessible_knowledge_ids],
'total': len(all_knowledge),
},
'tools': {
'items': [
{'id': t.id, 'name': t.name}
for t in all_tools
if t.id in accessible_tool_ids
],
'items': [{'id': t.id, 'name': t.name} for t in all_tools if t.id in accessible_tool_ids],
'total': len(all_tools),
},
}