5 Commits
main ... voksik

Author SHA1 Message Date
9bf9d8a55d add Custom provider 2025-12-14 06:31:00 +03:00
1e5105b7d6 fix glueAudio 2025-10-09 04:40:03 +03:00
bc82628147 added API compliance to the provider 2025-10-07 00:32:19 +03:00
bac78a9cee added API compliance to the provider 2025-10-06 22:50:05 +03:00
c2019b57a7 add new LLM provider io.net gemini g4f 2025-10-06 21:17:02 +03:00
13 changed files with 298 additions and 123 deletions

10
.gitignore vendored
View File

@@ -1,5 +1,5 @@
.env
__pycache__/
outputs/*
venv/
.env
__pycache__/
outputs/*
venv/

99
app.py
View File

@@ -1,17 +1,9 @@
import gradio as gr
# Загрузка параметров конфигурации
from config import *
# Подгрузка сервисов
from services.llm_factory import get_llm_provider
# Загрузка доп. модулей
from services.fasterWhisper import FasterWhisper
from handlers.gradioHandler import GradioHandlers
from handlers.fileHandlers import FileHandlers
from services.fasterWhisper import FasterWhisper
from handlers.convertMdToPdf import ConvertMdToPdf
from handlers.glueAudio import GlueAudio
@@ -19,16 +11,9 @@ gh = GradioHandlers(get_llm_provider, ConvertMdToPdf, FileHandlers, FasterWhispe
def main():
with gr.Blocks() as demo:
gr.HTML('''
<div align=center>
<h1>
Faster Whisper WebUI
</h1>
</div>
''')
gr.HTML('<div align=center><h1>Faster Whisper WebUI</h1></div>')
with gr.Row():
# Вкладка с основным взаимодействием
with gr.Tab('Actions'):
isPipelineEnabledCheckbox = gr.Checkbox(label='is pipeline enabled', value=True, interactive=True)
@@ -38,108 +23,106 @@ def main():
audioFiles = gr.Files(label='Load audio for transcribe', type="filepath")
images = gr.Files(label='Upload images', file_types=['image'])
recognizeBtn = gr.Button('recognize and integrate', variant='primary')
with gr.Accordion(label='Recognized text'):
recognizedText = gr.TextArea(label='')
with gr.Accordion(label='LLM'):
with gr.Column():
refineTextBtn = gr.Button('refine text', variant='secondary', interactive=False)
with gr.Accordion(label='Refined text raw'):
refinedText = gr.Textbox(label='', show_copy_button=True)
with gr.Accordion(label='Refined text md formated'):
refinedTextMD = gr.Markdown(label='')
# Вкладка с настройками
with gr.Tab('Settings'):
with gr.Column():
# Первое поле на всю ширину в акордионе настроек
with gr.Accordion('File settings'):
saveFileCheckbox = gr.Checkbox(label='save file', value=True, interactive=True)
filename = gr.Textbox(label='Output filename', value='output.txt', interactive=True)
filename = gr.Textbox(label='Output filename', value='output.md', interactive=True)
filenamePdf = gr.Textbox(label='Output filename for pdf', value='output.pdf', interactive=True)
# Акордион настроек faster whisper
with gr.Accordion(label='Faster whisper settings'):
with gr.Row():
# Левая колонка в акордионе
with gr.Column():
device = gr.Dropdown(label='Device', choices=DEVICES, value=DEVICES[1], interactive=True)
compute_type = gr.Dropdown(label='compute_type', choices=COMPUTE_TYPE, value=COMPUTE_TYPE[0], interactive=True)
fastWhisperModel = gr.Dropdown(label='Model', choices=FAST_WHISPER_MODELS, value=FAST_WHISPER_MODELS[11], interactive=True)
beamSize = gr.Number(label='beam_size', value=8, interactive=True)
noSpeechThreshold = gr.Number(label='no_speech_threshold', value=0.5, interactive=True)
vadFilter = gr.Checkbox(label='vad_filter', value=True, interactive=True)
wordTimestamps = gr.Checkbox(label='word_timestamps', value=True, interactive=True)
conditionOnPreviousText = gr.Checkbox(label='condition_on_previous_text', value=False, interactive=True)
# Правая колонка в акордионе
with gr.Column():
with gr.Accordion(label='Vad parameters'):
minSilenceDurationMs = gr.Number(label='min_silence_duration_ms', value=300, interactive=True)
speechPadMs = gr.Number(label='speech_pad_ms', value=200, interactive=True)
with gr.Accordion(label='Temperature'):
temp0 = gr.Number(label='temp_0', value=0.0, interactive=True)
temp1 = gr.Number(label='temp_1', value=0.2, interactive=True)
temp2 = gr.Number(label='temp_2', value=0.4, interactive=True)
# Нижний акордион настроек для api ключа llm
with gr.Accordion(label='LLM settings'):
apiKey = gr.Textbox(label='API key (required for io.net, Gemini)', value=DEFAULT_API_KEY, interactive=True)
with gr.Accordion(label='System prompt'):
systemPrompt = gr.Textbox(label='', value=DEFAULT_SYSTEM_PROMPT, interactive=True)
with gr.Row():
# ВЫБОР ПРОВАЙДЕРА
llmProvider = gr.Dropdown(
label='LLM Provider',
choices=LLM_PROVIDERS,
value=LLM_PROVIDERS[0],
interactive=True
)
# СПИСОК МОДЕЛЕЙ (теперь зависит от провайдера)
llmModel = gr.Dropdown(
label='Models',
choices=LLM_MODELS[LLM_PROVIDERS[0]], # Модели для провайдера по умолчанию
value=LLM_MODELS[LLM_PROVIDERS[0]][1],
interactive=True
)
llmProvider = gr.Dropdown(label='LLM Provider', choices=LLM_PROVIDERS, value=LLM_PROVIDERS[0], interactive=True)
llmModel = gr.Dropdown(label='Models', choices=LLM_MODELS[LLM_PROVIDERS[0]], value=LLM_MODELS[LLM_PROVIDERS[0]][1], interactive=True)
llmTemperature = gr.Number(label='Temperature', value=0.8, interactive=True)
# Настройки Custom провайдера
with gr.Accordion(label='Custom Provider Settings', open=True):
customBaseUrl = gr.Textbox(
label='Base URL',
value='http://127.0.0.1:1234/v1/',
interactive=True,
visible=False # Скрыто по умолчанию
)
# Обработчики событий
isPipelineEnabledCheckbox.change(gh.updateButton, inputs=[isPipelineEnabledCheckbox], outputs=refineTextBtn)
saveFileCheckbox.change(gh.updateTextbox, inputs=saveFileCheckbox, outputs=filename)
saveFileCheckbox.change(gh.updateTextbox, inputs=saveFileCheckbox, outputs=filenamePdf)
recognizeBtn.click(gh.handleRecognizeBtn, outputs=[recognizedText], inputs=[audioFiles, fastWhisperModel, device, compute_type, beamSize, vadFilter, minSilenceDurationMs, speechPadMs, temp0, temp1, temp2, wordTimestamps, noSpeechThreshold, conditionOnPreviousText, gr.State(GLUED_AUDIO_FILENAME), gr.State(OUTPUT_PATH)])
# Если пайплайн включен то тогда делаем автоматически
# автоматический пайплайн
recognizedText.change(
gh.generateByCondition,
inputs=[apiKey, llmProvider, llmModel, systemPrompt, recognizedText, llmTemperature, isPipelineEnabledCheckbox, gr.State("change"), saveFileCheckbox, filename, filenamePdf, gr.State(OUTPUT_PATH)],
outputs=[refinedText, refinedTextMD]
recognizeBtn.click(
gh.handleRecognizeBtn,
inputs=[audioFiles, fastWhisperModel, device, compute_type, beamSize,
vadFilter, minSilenceDurationMs, speechPadMs, temp0, temp1, temp2,
wordTimestamps, noSpeechThreshold, conditionOnPreviousText, gr.State(GLUED_AUDIO_FILENAME), gr.State(OUTPUT_PATH)],
outputs=[recognizedText],
)
# --- ИСПРАВЛЕНИЕ: ДОБАВЛЕН customBaseUrl В INPUTS ---
recognizedText.change(
gh.generateByCondition,
inputs=[apiKey, llmProvider, llmModel, systemPrompt, recognizedText, llmTemperature,
isPipelineEnabledCheckbox, gr.State("change"), saveFileCheckbox, filename, filenamePdf, gr.State(OUTPUT_PATH), customBaseUrl],
outputs=[refinedText, refinedTextMD]
)
# ручной запуск по кнопке
# Обновление выпадающего списка моделей и поля API ключа
llmProvider.change(
gh.update_model_dropdown,
inputs=llmProvider,
outputs=llmModel
outputs=[llmModel, apiKey]
)
# Переключение видимости URL для Custom провайдера
llmProvider.change(
fn=gh.toggle_custom_url,
inputs=llmProvider,
outputs=[customBaseUrl]
)
refineTextBtn.click(
gh.generateByCondition,
inputs=[apiKey, llmProvider, llmModel, systemPrompt, recognizedText, llmTemperature, isPipelineEnabledCheckbox, gr.State("click"), saveFileCheckbox, filename, filenamePdf, gr.State(OUTPUT_PATH)],
inputs=[apiKey, llmProvider, llmModel, systemPrompt, recognizedText, llmTemperature,
isPipelineEnabledCheckbox, gr.State("click"), saveFileCheckbox, filename, filenamePdf, gr.State(OUTPUT_PATH), customBaseUrl],
outputs=[refinedText, refinedTextMD]
)
demo.launch()
if __name__ == '__main__':
main()
main()

View File

@@ -8,10 +8,13 @@ DEVICES = ['cpu', 'cuda']
COMPUTE_TYPE = ['auto', 'int8', 'float16', 'float32']
# Стандартный API ключ
DEFAULT_API_KEY=os.getenv('API_KEY')
IO_API_KEY=os.getenv('IO_API_KEY')
GEMINI_API_KEY=os.getenv('GEMINI_API_KEY')
DEFAULT_API_KEY=IO_API_KEY
# Словарь провайдеров и их моделей
LLM_PROVIDERS = ['io.net', 'Gemini', 'gpt4free']
LLM_PROVIDERS = ['io.net', 'Gemini', 'gpt4free', 'Custom']
LLM_MODELS = {
'io.net': [
'openai/gpt-oss-120b', 'Qwen/Qwen3-235B-A22B-Thinking-2507',
@@ -27,7 +30,7 @@ LLM_MODELS = {
'gemini-2.5-flash',
'gemini-2.5-flash-lite'
],
'gpt4free': [ # Модели могут меняться, проверьте документацию g4f
'gpt4free': [
'default',
'gpt-4',
'sonar-reasoning',
@@ -38,7 +41,14 @@ LLM_MODELS = {
'gpt-4o-mini',
'deepseek-r1',
'PollinationsAI:gpt-5-nano'
]
],
'Custom': [
'qwen/qwen3-vl-30b',
'qwen/qwen3-coder-30b',
'openai/gpt-oss-20b',
'qwen3-vl-8b-thinking',
'qwen/qwen3-vl-8b',
],
}
# Задаем выходную директорию

View File

@@ -1,11 +1,61 @@
from pydub import AudioSegment
import subprocess
from pathlib import Path
class GlueAudio():
def glue(self, audioFiles):
glued = AudioSegment.empty()
def glue(self, audio_files: list, output_path: str, output_filename: str) -> Path:
"""
Склеивает аудиофайлы РАЗНЫХ форматов с помощью FFmpeg и filter_complex.
Это универсальный и эффективный по памяти метод.
for audioFile in audioFiles:
audio = AudioSegment.from_file(audioFile)
glued += audio
Args:
audio_files (list): Список путей к исходным аудиофайлам.
output_path (str): Директория для сохранения итогового файла.
output_filename (str): Имя итогового склеенного файла.
return glued
Returns:
Path: Путь к созданному склеенному файлу.
"""
output_dir = Path(output_path)
output_dir.mkdir(parents=True, exist_ok=True)
final_audio_path = output_dir / output_filename
if not audio_files:
raise ValueError("Список аудиофайлов для склейки пуст.")
# 1. Формируем часть команды с входными файлами (-i file1 -i file2 ...)
input_args = []
for file_path in audio_files:
input_args.extend(['-i', str(Path(file_path).resolve())])
# 2. Формируем строку для filter_complex
num_files = len(audio_files)
stream_specifiers = "".join([f"[{i}:a]" for i in range(num_files)])
filter_complex_str = f"{stream_specifiers}concat=n={num_files}:v=0:a=1[outa]"
# 3. Собираем полную команду
command = [
'ffmpeg',
*input_args, # Распаковываем список входных файлов
'-filter_complex', filter_complex_str,
'-map', '[outa]',
'-c:a', 'libmp3lame',
'-q:a', '2',
str(final_audio_path),
'-y'
]
try:
# 4. Выполняем команду
print(f"Выполнение команды FFmpeg: {' '.join(command)}")
subprocess.run(command, check=True, capture_output=True, text=True)
print("FFmpeg успешно завершил склейку.")
except FileNotFoundError:
raise FileNotFoundError("FFmpeg не найден. Убедитесь, что он установлен и доступен в системной переменной PATH.")
except subprocess.CalledProcessError as e:
print("Ошибка при выполнении FFmpeg!")
print("Stderr:", e.stderr)
raise RuntimeError(f"Ошибка FFmpeg при склейке файлов: {e.stderr}")
return final_audio_path

View File

@@ -1,34 +1,52 @@
from config import LLM_MODELS # Импортируем словарь моделей
from config import LLM_MODELS, GEMINI_API_KEY, IO_API_KEY
import gradio as gr
class GradioHandlers:
def __init__(self, llm_factory, ConvertMdToPdf, FileHandlers, FasterWhisper, GlueAudio):
# Объект для работы с файлами
self.fh = FileHandlers()
self.ga = GlueAudio()
self.ConvertMdToPdf = ConvertMdToPdf()
self.FasterWhisper = FasterWhisper()
self.llm_factory = llm_factory # Сохраняем фабрику
self.llm_factory = llm_factory
def handleRecognizeBtn(self, audioFiles, model, device, compute_type, beamSize, vadFilter, minSilenceDurationMs, speechPadMs, temp0, temp1, temp2, wordTimestamps, noSpeechThreshold, conditionOnPreviousText, filename, outPath):
audioFile = self.ga.glue(audioFiles)
file = self.fh.saveFile(filename, audioFile, outPath)
return self.FasterWhisper.recognize(model, device, compute_type, file, beamSize, vadFilter, minSilenceDurationMs, speechPadMs, temp0, temp1, temp2, wordTimestamps, noSpeechThreshold, conditionOnPreviousText)
# Функция улучшения текста
def generateByCondition(self, api_key, llm_provider, llm_model, system_prompt, recognized_text, llm_temperature, is_pipeline_enabled, trigger, isSaveFile, filename, filenamePdf, output_path):
def handleRecognizeBtn(self, audioFiles, model, device, compute_type, beamSize, vadFilter,
minSilenceDurationMs, speechPadMs, temp0, temp1, temp2,
wordTimestamps, noSpeechThreshold, conditionOnPreviousText, filename, outPath):
try:
# Получаем нужный провайдер через фабрику
provider = self.llm_factory(llm_provider, api_key)
glued_audio_path = self.ga.glue(
audio_files=[f.name for f in audioFiles],
output_path=outPath,
output_filename=filename
)
except (FileNotFoundError, RuntimeError) as e:
gr.Warning(str(e))
return ""
return self.FasterWhisper.recognize(model, device, compute_type, str(glued_audio_path), beamSize, vadFilter, minSilenceDurationMs, speechPadMs, temp0, temp1, temp2, wordTimestamps, noSpeechThreshold, conditionOnPreviousText)
# Добавил аргумент custom_base_url в конец
def generateByCondition(self, api_key, llm_provider,
llm_model, system_prompt, recognized_text,
llm_temperature, is_pipeline_enabled, trigger,
isSaveFile, filename, filenamePdf, output_path, custom_base_url):
try:
if llm_provider == "Custom":
# Передаем base_url только для Custom
provider = self.llm_factory(llm_provider, api_key, base_url=custom_base_url)
else:
provider = self.llm_factory(llm_provider, api_key)
except ValueError as e:
# Если API ключ не предоставлен для нужного провайдера, выводим ошибку
self.gr.Warning(str(e))
return self.gr.skip(), self.gr.skip()
gr.Warning(str(e))
return gr.skip(), gr.skip()
def process():
result, md = provider.generate(llm_model, system_prompt, recognized_text, llm_temperature)
# Добавлена обработка ошибок генерации
try:
result, md = provider.generate(llm_model, system_prompt, recognized_text, llm_temperature)
except Exception as e:
raise gr.Error(f"Ошибка генерации LLM: {e}")
pdf, unicodeText = self.ConvertMdToPdf.convertLatexToText(md)
if isSaveFile:
self.fh.saveFile(filenamePdf, pdf, output_path)
@@ -40,28 +58,30 @@ class GradioHandlers:
return gr.skip(), gr.skip()
# НОВАЯ ФУНКЦИЯ для обновления списка моделей
def update_model_dropdown(self, provider):
"""
Вызывается при изменении llmProvider.
Возвращает обновленный компонент Dropdown для моделей.
"""
# Получаем список моделей для выбранного провайдера
models = LLM_MODELS.get(provider, [])
# Выбираем первое значение по умолчанию, если список не пуст
default_value = models[0] if models else None
# Возвращаем обновленный компонент. Используем 'gr' напрямую.
return gr.update(choices=models, value=default_value)
# Обновляем список моделей и настройки поля API Key
if provider == 'io.net':
return gr.update(choices=models, value=default_value), gr.update(label='API key', value=IO_API_KEY, interactive=True, visible=True)
if provider == 'Gemini':
return gr.update(choices=models, value=default_value), gr.update(label='API key', value=GEMINI_API_KEY, interactive=True, visible=True)
if provider == 'gpt4free':
return gr.update(choices=models, value=default_value), gr.update(label='API key (not required)', value="", interactive=False, visible=True)
if provider == "Custom":
return gr.update(choices=models, value=default_value), gr.update(label='API key (optional)', value="", interactive=True, visible=True) # Для Custom ключ может понадобиться
# Функция для динамического обновления кнопки
def updateButton(self, isChecked):
if not isChecked:
variant = 'primary'
else:
variant = 'secondary'
variant = 'secondary' if isChecked else 'primary'
return gr.update(interactive=not isChecked, variant=variant)
def toggle_custom_url(self, provider):
"""Показывает поле Base URL только если выбран Custom"""
return gr.update(visible=(provider == 'Custom'))
def update_custom_url(self, base_url):
return None
def updateTextbox(self, isChecked):
return gr.update(visible=isChecked)

View File

@@ -0,0 +1,21 @@
import os
from PIL import Image, ExifTags
import subprocess
import json
from datetime import datetime
class MetadataHandler:
def get_image_timestamp(self, image_path: str) -> datetime | None:
"""Извлекает метку времени из метаданных изображения, если она доступна."""
try:
image = Image.open(image_path)
exif_data = image._getexif()
if exif_data:
for tag, value in exif_data.items():
decoded_tag = ExifTags.TAGS.get(tag, tag)
if decoded_tag == 'DateTimeOriginal':
return value
return None
except Exception as e:
print(f"Error extracting metadata from image: {e}")
return None

7
pandoc.txt Normal file
View File

@@ -0,0 +1,7 @@
pandoc "out.md" -o output1310.pdf \
--pdf-engine=xelatex \
-V geometry:margin=2.5cm \
-V fontsize=12pt \
-V mainfont="Times New Roman" \
-V colorlinks=true \
-V linkcolor=blue\

View File

@@ -1,7 +1,11 @@
from faster_whisper import WhisperModel
class FasterWhisper:
def recognize(self, model, device, compute_type, audioFile, beamSize, vadFilter, minSilenceDurationMs, speechPadMs, temp0, temp1, temp2, wordTimestamps, noSpeechThreshold, conditionOnPreviousText):
def recognize(self, model, device, compute_type,
audioFile, beamSize, vadFilter,
minSilenceDurationMs, speechPadMs,
temp0, temp1, temp2, wordTimestamps,
noSpeechThreshold, conditionOnPreviousText):
model = WhisperModel(model, device=device, compute_type=compute_type) # Задаем модель
segments, _ = model.transcribe( # Распознаем текст
@@ -22,6 +26,7 @@ class FasterWhisper:
for seg in segments:
text += f"[{self.format_timestamp(seg.start)} -> {self.format_timestamp(seg.end)}] {seg.text}" + '\n'
print(f"[{self.format_timestamp(seg.start)} -> {self.format_timestamp(seg.end)}] {seg.text}")
return text

View File

@@ -3,8 +3,9 @@ from services.llm_providers.ionet_provider import IoNetProvider
from services.llm_providers.gemini_provider import GeminiProvider
from services.llm_providers.gpt4free_provider import Gpt4FreeProvider
from services.llm_providers.base_provider import BaseLLMProvider
from services.llm_providers.custom_provider import CustomProvider
def get_llm_provider(provider_name: str, api_key: str | None) -> BaseLLMProvider:
def get_llm_provider(provider_name: str, api_key: str | None = None, base_url: str | None = None) -> BaseLLMProvider:
"""
Фабричная функция для получения экземпляра провайдера LLM.
"""
@@ -18,5 +19,9 @@ def get_llm_provider(provider_name: str, api_key: str | None) -> BaseLLMProvider
return GeminiProvider(api_key)
elif provider_name == 'gpt4free':
return Gpt4FreeProvider()
elif provider_name == 'Custom':
if not base_url:
raise ValueError("Base URL обязателен для Custom провайдера")
return CustomProvider(api_key, base_url) # base_url будет установлен позже
else:
raise ValueError(f"Неизвестный провайдер: {provider_name}")

View File

@@ -0,0 +1,44 @@
import requests
from .base_provider import BaseLLMProvider
class CustomProvider(BaseLLMProvider):
def __init__(self, api_key: str | None = None, base_url: str | None = None):
super().__init__(api_key)
self.base_url = base_url or 'http://127.0.0.1:1234/v1/'
# ГАРАНТИРУЕМ наличие слеша в конце URL
if not self.base_url.endswith('/'):
self.base_url += '/'
def generate(self, model: str, system_prompt: str, user_prompt: str, temp: float):
url = f"{self.base_url}chat/completions"
# Некоторые Custom провайдеры (как vLLM или Ollama) могут требовать API Key, даже если он фиктивный
headers = {"Content-Type": "application/json"}
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
data = {
"model": model,
"messages": [
{'role': 'system', 'content': system_prompt},
{'role': 'user', 'content': user_prompt},
],
"temperature": temp,
"stream": False
}
try:
response = requests.post(url, headers=headers, json=data, timeout=300)
response.raise_for_status()
result = response.json()
# Обработка разных форматов ответа (на всякий случай)
if 'choices' in result and len(result['choices']) > 0:
text = str(result['choices'][0]['message']['content'])
return text, text
else:
return f"Неожиданный ответ от сервера: {result}", str(result)
except requests.exceptions.RequestException as e:
return f"Ошибка при запросе к CustomProvider API: {e}", f"Ошибка: {e}"

View File

@@ -41,7 +41,7 @@ class GeminiProvider(BaseLLMProvider):
try:
# 4. Отправляем POST-запрос с данными и настройками прокси
response = requests.post(api_url, json=data, proxies=proxies, timeout=90)
response = requests.post(api_url, json=data, proxies=proxies, timeout=400)
# Проверяем, не вернул ли сервер ошибку (например, 4xx или 5xx)
response.raise_for_status()

View File

@@ -3,6 +3,13 @@ from g4f.client import Client
from .base_provider import BaseLLMProvider
class Gpt4FreeProvider(BaseLLMProvider):
"""
Провайдер для работы с моделью GPT через библиотеку gpt4free.
Этот класс реализует интерфейс BaseLLMProvider и предоставляет возможность
взаимодействия с различными LLM через сервис gpt4free, который не требует
API ключа для работы.
"""
# gpt4free не требует API ключа
def __init__(self, api_key: str | None = None):
super().__init__(api_key)
@@ -10,6 +17,18 @@ class Gpt4FreeProvider(BaseLLMProvider):
def generate(self, model: str, system_prompt: str, user_prompt: str, temp: float):
"""
Генерирует ответ от модели GPT с использованием gpt4free.
Args:
model (str): Название модели для генерации ответа
system_prompt (str): Системное сообщение для контекста
user_prompt (str): Пользовательский запрос
temp (float): Температура генерации ( controls randomness of responses)
Returns:
tuple: Кортеж из двух одинаковых строк - сгенерированного ответа и его копии
"""
# temp в g4f может работать не для всех внутренних провайдеров
try:
response = self.client.chat.completions.create(

View File

@@ -1,23 +1,34 @@
import openai
import requests
from .base_provider import BaseLLMProvider
class IoNetProvider(BaseLLMProvider):
def __init__(self, api_key: str):
super().__init__(api_key)
self.client = openai.OpenAI(
api_key=self.api_key,
base_url='https://api.intelligence.io.solutions/api/v1/'
)
self.base_url = 'https://api.intelligence.io.solutions/api/v1'
def generate(self, model: str, system_prompt: str, user_prompt: str, temp: float):
response = self.client.chat.completions.create(
model=model,
messages=[
url = f"{self.base_url}/chat/completions"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
}
data = {
"model": model,
"messages": [
{'role': 'system', 'content': system_prompt},
{'role': 'user', 'content': user_prompt},
],
temperature=temp,
stream=False
)
text = str(response.choices[0].message.content)
return text, text # Возвращаем как чистый текст, так и Markdown
"temperature": temp
}
try:
response = requests.post(url, headers=headers, json=data)
response.raise_for_status()
result = response.json()
text = str(result['choices'][0]['message']['content'])
return text, text # Возвращаем как чистый текст, так и Markdown
except requests.exceptions.RequestException as e:
raise Exception(f"Ошибка при запросе к IO.net API: {e}")