Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
# LLM Provider Selection
LLM_PROVIDER=openai
# Options: openai, groq, gemini
# Options: openai, groq, gemini, minimax

# LLM Model Configurations
OPENAI_MODEL=gpt-4o
GROQ_MODEL=llama3-70b-8192
GEMINI_MODEL=gemini-2.5-flash
MINIMAX_MODEL=MiniMax-M2.7

# API Keys (Required based on LLM_PROVIDER)
OPENAI_API_KEY=your_openai_api_key_here
GROQ_API_KEY=your_groq_api_key_here
GEMINI_API_KEY=your_gemini_api_key_here
MINIMAX_API_KEY=your_minimax_api_key_here

# Pexels API Key (Always required)
PEXELS_API_KEY=your_pexels_api_key_here
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ https://github.com/user-attachments/assets/1e440ace-8560-4e12-850e-c532740711e7
## Features

- **AI-Powered Script Generation** - Automatically generates engaging scripts from any topic
- **Multiple LLM Providers** - Choose from OpenAI, Groq, or Google Gemini
- **Multiple LLM Providers** - Choose from OpenAI, Groq, Google Gemini, or MiniMax
- **Text-to-Speech** - Natural-sounding voiceovers with EdgeTTS (free) or ElevenLabs
- **Automatic B-Roll** - Fetches relevant background videos from Pexels
- **Customizable Captions** - Full control over font, color, position, and styling
Expand Down Expand Up @@ -81,13 +81,14 @@ All settings are configured via the `.env` file. Copy `.env.example` to get star
| OpenAI | If using OpenAI | [platform.openai.com](https://platform.openai.com/api-keys) |
| Groq | If using Groq | [console.groq.com](https://console.groq.com/keys) |
| Google Gemini | If using Gemini | [makersuite.google.com](https://makersuite.google.com/app/apikey) |
| MiniMax | If using MiniMax | [platform.minimaxi.com](https://platform.minimaxi.com/user-center/basic-information/interface-key) |
| Deepgram | If using Deepgram STT | [console.deepgram.com](https://console.deepgram.com/) |
| ElevenLabs | If using ElevenLabs TTS | [elevenlabs.io](https://elevenlabs.io/) |

### Provider Selection

```env
# LLM Provider: openai, groq, or gemini
# LLM Provider: openai, groq, gemini, or minimax
LLM_PROVIDER=openai

# Text-to-Speech: edgetts (free) or elevenlabs
Expand Down
Empty file added tests/__init__.py
Empty file.
103 changes: 103 additions & 0 deletions tests/test_minimax_integration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"""Integration tests for MiniMax LLM provider.

These tests require a valid MINIMAX_API_KEY environment variable.
Skip automatically when the key is not set.
"""

import os
import json
import re
import unittest

MINIMAX_API_KEY = os.getenv("MINIMAX_API_KEY")
SKIP_REASON = "MINIMAX_API_KEY not set"


def strip_think_tags(text):
"""Strip <think>...</think> tags from MiniMax M2.7 responses."""
return re.sub(r"<think>.*?</think>\s*", "", text, flags=re.DOTALL).strip()


def extract_json(text):
"""Extract JSON from response, handling think tags and markdown fences."""
text = strip_think_tags(text)
if text.startswith("```"):
text = text.split("\n", 1)[1].rsplit("```", 1)[0].strip()
json_start = text.find("{")
json_end = text.rfind("}")
if json_start >= 0 and json_end >= 0:
text = text[json_start : json_end + 1]
return json.loads(text)


@unittest.skipUnless(MINIMAX_API_KEY, SKIP_REASON)
class TestMiniMaxIntegration(unittest.TestCase):
"""Integration tests that hit the real MiniMax API."""

def test_chat_completions_basic(self):
"""MiniMax should respond to a basic chat completion request."""
from openai import OpenAI

client = OpenAI(
api_key=MINIMAX_API_KEY,
base_url="https://api.minimax.io/v1",
)
response = client.chat.completions.create(
model="MiniMax-M2.7",
messages=[
{"role": "user", "content": "Say hello in one word."}
],
max_tokens=200,
)
self.assertTrue(len(response.choices) > 0)
content = strip_think_tags(response.choices[0].message.content)
self.assertTrue(len(content) > 0)

def test_json_output(self):
"""MiniMax should return valid JSON when prompted."""
from openai import OpenAI

client = OpenAI(
api_key=MINIMAX_API_KEY,
base_url="https://api.minimax.io/v1",
)
response = client.chat.completions.create(
model="MiniMax-M2.7",
messages=[
{"role": "system", "content": "Output valid JSON only. No markdown. No thinking."},
{"role": "user", "content": 'Return {"greeting": "hello"}'},
],
max_tokens=50,
)
content = response.choices[0].message.content.strip()
parsed = extract_json(content)
self.assertIn("greeting", parsed)

def test_script_generation_format(self):
"""MiniMax should generate a script in the expected JSON format."""
from openai import OpenAI

client = OpenAI(
api_key=MINIMAX_API_KEY,
base_url="https://api.minimax.io/v1",
)
prompt = (
"You are a content writer. Generate a very short 2-sentence fact. "
'Output ONLY a JSON object: {"script": "your text here"}'
)
response = client.chat.completions.create(
model="MiniMax-M2.7",
messages=[
{"role": "system", "content": prompt},
{"role": "user", "content": "space facts"},
],
max_tokens=200,
)
content = response.choices[0].message.content.strip()
parsed = extract_json(content)
self.assertIn("script", parsed)
self.assertTrue(len(parsed["script"]) > 10)


if __name__ == "__main__":
unittest.main()
Loading