$ mkdir llm_tts_backend
$ cd llm_tts_backend
$ django-admin startproject config .
$ manage.py startapp api
requirements.txt
Django>=4.2.0 requests>=2.31.0 gTTS>=2.3.2 python-dotenv>=1.0.0 django-cors-headers>=4.3.0 EOF
$ pip3 install -r requirements.txt
.envファイルの作成
$ cat > .env << 'EOF'
DIFY_API_KEY=your_dify_api_key_here
APP_ID=your_app_id_here
SECRET_KEY=django-insecure-your-secret-key-here
DEBUG=True
EOF
django-insecure-your-secret-key-here は以下のように発行する
$ python3 -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'
config/settings.py
[code]
import os
from pathlib import Path
from dotenv import load_dotenv
load_dotenv()
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get('SECRET_KEY', 'django-insecure-default-key')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.environ.get('DEBUG', 'False') == 'True'
ALLOWED_HOSTS = ['*']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'corsheaders',
'api',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000",
"http://localhost:8080",
"http://127.0.0.1:3000",
"http://127.0.0.1:8080",
"http://192.168.33.10:3000",
"http://192.168.33.10:8080",
]
CORS_ALLOW_CREDENTIALS = True
CSRF_COOKIE_HTTPONLY = False
CSRF_COOKIE_SAMESITE = 'Lax'
ROOT_URLCONF = 'config.urls'
[/code]
api/views.py
[code]
from django.http import JsonResponse, FileResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from gtts import gTTS
import requests
import json
import os
import tempfile
from datetime import datetime
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.environ.get("DIFY_API_KEY")
BASE_URL = "https://api.dify.ai/v1"
CHAT_ENDPOINT = f"{BASE_URL}/chat-messages"
# Create your views here.
@csrf_exempt
@require_http_methods(["POST"])
def process_query(request):
"""
フロントエンドからテキストを受け取り、Dify LLM処理後、TTSで音声を返す
"""
try:
data = json.loads(request.body)
if not data or 'text' not in data:
return JsonResponse({'error': 'text パラメータが必要です'}, status=400)
user_text = data['text']
lang = data.get('lang', 'ja')
slow = data.get('slow', False)
if not user_text.strip():
return JsonResponse({'error': 'テキストが空です'}, status=400)
# --- Dify LLM処理 ---
payload = {
"query": user_text,
"inputs": {"context": "Null"},
"user": f"user_django_{datetime.now().strftime('%Y%m%d%H%M%S')}",
"response_mode": "blocking",
}
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
llm_response = requests.post(
CHAT_ENDPOINT,
headers=headers,
data=json.dumps(payload),
timeout=30
)
llm_response.raise_for_status()
llm_data = llm_response.json()
if not llm_data or 'answer' not in llm_data:
return JsonResponse({'error': 'LLMからの回答が取得できませんでした'}, status=500)
llm_answer = llm_data['answer']
# --- TTS処理 ---
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.mp3')
temp_filename = temp_file.name
temp_file.close()
tts = gTTS(text=llm_answer, lang=lang, slow=slow)
tts.save(temp_filename)
response = FileResponse(
open(temp_filename, 'rb'),
content_type='audio/mpeg',
as_attachment=True,
filename=f'speech_{datetime.now().strftime("%Y%m%d_%H%M%S")}.mp3'
)
response['X-LLM-Answer'] = llm_answer
return response
except requests.exceptions.RequestException as e:
return JsonResponse({'error': f'LLM API エラー: {str(e)}'}, status=500)
except Exception as e:
return JsonResponse({'error': f'サーバーエラー: {str(e)}'}, status=500)
@require_http_methods(["GET"])
def health_check(request):
"""ヘルスチェック用エンドポイント"""
return JsonResponse({'status': 'ok'})
@require_http_methods(["GET"])
def get_languages(request):
"""サポートされている言語のリストを返す"""
languages = {
'ja': '日本語',
'en': '英語',
'zh-cn': '中国語(簡体字)',
'zh-tw': '中国語(繁体字)',
'ko': '韓国語',
'es': 'スペイン語',
'fr': 'フランス語',
'de': 'ドイツ語',
'it': 'イタリア語',
'pt': 'ポルトガル語',
}
return JsonResponse(languages)
[/code]
api/urls.py
[code]
from django.urls import path
from . import views
urlpatterns = [
path('process', views.process_query, name='process_query'),
path('health', views.health_check, name='health_check'),
path('languages', views.get_languages, name='get_languages'),
]
[/code]
config/urls.py
[code]
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('api.urls')),
]
[/code]
ステップ11: データベースのマイグレーション
$ python3 manage.py migrate
ステップ12: 開発サーバ
$ python3 manage.py runserver
$ curl http://localhost:8000/api/health
$ curl http://localhost:8000/api/languages00/api/languages
{"ja": "\u65e5\u672c\u8a9e", "en": "\u82f1\u8a9e", "zh-cn": "\u4e2d\u56fd\u8a9e(\u7c21\u4f53\u5b57)", "zh-tw": "\u4e2d\u56fd\u8a9e(\u7e41\u4f53\u5b57)", "ko": "\u97d3\u56fd\u8a9e", "es": "\u30b9\u30da\u30a4\u30f3\u8a9e", "fr": "\u30d5\u30e9\u30f3\u30b9\u8a9e", "de": "\u30c9\u30a4\u30c4\u8a9e", "it": "\u30a4\u30bf\u30ea\u30a2\u8a9e", "pt": "\u30dd\u30eb\u30c8\u30ac\u30eb\u8a9e"}
curl -X POST http://localhost:8000/api/process \
-H "Content-Type: application/json" \
-d '{"text": "こんにちは"}' \
--output test_response.mp3



