mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-01-11 20:10:25 +00:00
updated AI model
This commit is contained in:
parent
01a041aebf
commit
76310dae1b
2 changed files with 741 additions and 467 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -25,6 +25,18 @@ export interface MovieContext {
|
||||||
runtime?: number;
|
runtime?: number;
|
||||||
tagline?: string;
|
tagline?: string;
|
||||||
keywords?: string[];
|
keywords?: string[];
|
||||||
|
voteAverage?: number;
|
||||||
|
voteCount?: number;
|
||||||
|
popularity?: number;
|
||||||
|
budget?: number;
|
||||||
|
revenue?: number;
|
||||||
|
productionCompanies?: string[];
|
||||||
|
productionCountries?: string[];
|
||||||
|
spokenLanguages?: string[];
|
||||||
|
originalLanguage?: string;
|
||||||
|
status?: string;
|
||||||
|
contentRating?: string;
|
||||||
|
imdbId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EpisodeContext {
|
export interface EpisodeContext {
|
||||||
|
|
@ -50,6 +62,12 @@ export interface EpisodeContext {
|
||||||
name: string;
|
name: string;
|
||||||
character: string;
|
character: string;
|
||||||
}>;
|
}>;
|
||||||
|
// New enhanced fields
|
||||||
|
voteAverage?: number;
|
||||||
|
showGenres?: string[];
|
||||||
|
showNetworks?: string[];
|
||||||
|
showStatus?: string;
|
||||||
|
contentRating?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SeriesContext {
|
export interface SeriesContext {
|
||||||
|
|
@ -76,7 +94,19 @@ export interface SeriesContext {
|
||||||
airDate: string;
|
airDate: string;
|
||||||
released: boolean;
|
released: boolean;
|
||||||
overview?: string;
|
overview?: string;
|
||||||
|
voteAverage?: number;
|
||||||
}>>;
|
}>>;
|
||||||
|
// New enhanced fields
|
||||||
|
networks?: string[];
|
||||||
|
status?: string;
|
||||||
|
originalLanguage?: string;
|
||||||
|
popularity?: number;
|
||||||
|
voteAverage?: number;
|
||||||
|
voteCount?: number;
|
||||||
|
createdBy?: string[];
|
||||||
|
contentRating?: string;
|
||||||
|
productionCompanies?: string[];
|
||||||
|
type?: string; // "Scripted", "Documentary", etc.
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ContentContext = MovieContext | EpisodeContext | SeriesContext;
|
export type ContentContext = MovieContext | EpisodeContext | SeriesContext;
|
||||||
|
|
@ -101,7 +131,7 @@ class AIService {
|
||||||
private apiKey: string | null = null;
|
private apiKey: string | null = null;
|
||||||
private baseUrl = 'https://openrouter.ai/api/v1';
|
private baseUrl = 'https://openrouter.ai/api/v1';
|
||||||
|
|
||||||
private constructor() {}
|
private constructor() { }
|
||||||
|
|
||||||
static getInstance(): AIService {
|
static getInstance(): AIService {
|
||||||
if (!AIService.instance) {
|
if (!AIService.instance) {
|
||||||
|
|
@ -130,7 +160,7 @@ class AIService {
|
||||||
private createSystemPrompt(context: ContentContext): string {
|
private createSystemPrompt(context: ContentContext): string {
|
||||||
const isSeries = 'episodesBySeason' in (context as any);
|
const isSeries = 'episodesBySeason' in (context as any);
|
||||||
const isEpisode = !isSeries && 'showTitle' in (context as any);
|
const isEpisode = !isSeries && 'showTitle' in (context as any);
|
||||||
|
|
||||||
if (isSeries) {
|
if (isSeries) {
|
||||||
const series = context as SeriesContext;
|
const series = context as SeriesContext;
|
||||||
const currentDate = new Date().toISOString().split('T')[0];
|
const currentDate = new Date().toISOString().split('T')[0];
|
||||||
|
|
@ -148,11 +178,19 @@ CRITICAL: Today's date is ${currentDate}. Use ONLY the verified information prov
|
||||||
|
|
||||||
VERIFIED CURRENT SERIES INFORMATION FROM DATABASE:
|
VERIFIED CURRENT SERIES INFORMATION FROM DATABASE:
|
||||||
- Title: ${series.title}
|
- Title: ${series.title}
|
||||||
|
- Original Language: ${series.originalLanguage || 'Unknown'}
|
||||||
|
- Status: ${series.status || 'Unknown'}
|
||||||
- First Air Date: ${series.firstAirDate || 'Unknown'}
|
- First Air Date: ${series.firstAirDate || 'Unknown'}
|
||||||
- Last Air Date: ${series.lastAirDate || 'Unknown'}
|
- Last Air Date: ${series.lastAirDate || 'Unknown'}
|
||||||
- Seasons: ${series.totalSeasons}
|
- Seasons: ${series.totalSeasons}
|
||||||
- Episodes: ${series.totalEpisodes}
|
- Episodes: ${series.totalEpisodes}
|
||||||
|
- Classification: ${series.type || 'Scripted'}
|
||||||
|
- Content Rating: ${series.contentRating || 'Not Rated'}
|
||||||
- Genres: ${series.genres.join(', ') || 'Unknown'}
|
- Genres: ${series.genres.join(', ') || 'Unknown'}
|
||||||
|
- TMDB Rating: ${series.voteAverage ? `${series.voteAverage}/10 (${series.voteCount} votes)` : 'N/A'}
|
||||||
|
- Popularity Score: ${series.popularity || 'N/A'}
|
||||||
|
- Created By: ${series.createdBy?.join(', ') || 'Unknown'}
|
||||||
|
- Production: ${series.productionCompanies?.join(', ') || 'Unknown'}
|
||||||
- Synopsis: ${series.overview || 'No synopsis available'}
|
- Synopsis: ${series.overview || 'No synopsis available'}
|
||||||
|
|
||||||
Cast:
|
Cast:
|
||||||
|
|
@ -192,6 +230,11 @@ VERIFIED CURRENT INFORMATION FROM DATABASE:
|
||||||
- Air Date: ${ep.airDate || 'Unknown'}
|
- Air Date: ${ep.airDate || 'Unknown'}
|
||||||
- Release Status: ${ep.released ? 'RELEASED AND AVAILABLE FOR VIEWING' : 'Not Yet Released'}
|
- Release Status: ${ep.released ? 'RELEASED AND AVAILABLE FOR VIEWING' : 'Not Yet Released'}
|
||||||
- Runtime: ${ep.runtime ? `${ep.runtime} minutes` : 'Unknown'}
|
- Runtime: ${ep.runtime ? `${ep.runtime} minutes` : 'Unknown'}
|
||||||
|
- TMDB Rating: ${ep.voteAverage ? `${ep.voteAverage}/10` : 'N/A'}
|
||||||
|
- Show Content Rating: ${ep.contentRating || 'Not Rated'}
|
||||||
|
- Show Genres: ${ep.showGenres?.join(', ') || 'Unknown'}
|
||||||
|
- Network: ${ep.showNetworks?.join(', ') || 'Unknown'}
|
||||||
|
- Show Status: ${ep.showStatus || 'Unknown'}
|
||||||
- Synopsis: ${ep.overview || 'No synopsis available'}
|
- Synopsis: ${ep.overview || 'No synopsis available'}
|
||||||
|
|
||||||
Cast:
|
Cast:
|
||||||
|
|
@ -227,11 +270,22 @@ CRITICAL: Today's date is ${currentDate}. Use ONLY the verified information prov
|
||||||
|
|
||||||
VERIFIED CURRENT MOVIE INFORMATION FROM DATABASE:
|
VERIFIED CURRENT MOVIE INFORMATION FROM DATABASE:
|
||||||
- Title: ${movie.title}
|
- Title: ${movie.title}
|
||||||
|
- Original Language: ${movie.originalLanguage || 'Unknown'}
|
||||||
|
- Status: ${movie.status || 'Unknown'}
|
||||||
- Release Date: ${movie.releaseDate || 'Unknown'}
|
- Release Date: ${movie.releaseDate || 'Unknown'}
|
||||||
|
- Content Rating: ${movie.contentRating || 'Not Rated'}
|
||||||
- Runtime: ${movie.runtime ? `${movie.runtime} minutes` : 'Unknown'}
|
- Runtime: ${movie.runtime ? `${movie.runtime} minutes` : 'Unknown'}
|
||||||
- Genres: ${movie.genres.join(', ') || 'Unknown'}
|
- Genres: ${movie.genres.join(', ') || 'Unknown'}
|
||||||
|
- TMDB Rating: ${movie.voteAverage ? `${movie.voteAverage}/10 (${movie.voteCount} votes)` : 'N/A'}
|
||||||
|
- Popularity Score: ${movie.popularity || 'N/A'}
|
||||||
|
- Budget: ${movie.budget && movie.budget > 0 ? `$${movie.budget.toLocaleString()}` : 'Unknown'}
|
||||||
|
- Revenue: ${movie.revenue && movie.revenue > 0 ? `$${movie.revenue.toLocaleString()}` : 'Unknown'}
|
||||||
|
- Production: ${movie.productionCompanies?.join(', ') || 'Unknown'}
|
||||||
|
- Countries: ${movie.productionCountries?.join(', ') || 'Unknown'}
|
||||||
|
- Spoken Languages: ${movie.spokenLanguages?.join(', ') || 'Unknown'}
|
||||||
- Tagline: ${movie.tagline || 'N/A'}
|
- Tagline: ${movie.tagline || 'N/A'}
|
||||||
- Synopsis: ${movie.overview || 'No synopsis available'}
|
- Synopsis: ${movie.overview || 'No synopsis available'}
|
||||||
|
- IMDb ID: ${movie.imdbId || 'N/A'}
|
||||||
|
|
||||||
Cast:
|
Cast:
|
||||||
${movie.cast.map(c => `- ${c.name} as ${c.character}`).join('\n')}
|
${movie.cast.map(c => `- ${c.name} as ${c.character}`).join('\n')}
|
||||||
|
|
@ -261,8 +315,8 @@ Answer questions about this movie using only the verified database information a
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendMessage(
|
async sendMessage(
|
||||||
message: string,
|
message: string,
|
||||||
context: ContentContext,
|
context: ContentContext,
|
||||||
conversationHistory: ChatMessage[] = []
|
conversationHistory: ChatMessage[] = []
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
if (!await this.isConfigured()) {
|
if (!await this.isConfigured()) {
|
||||||
|
|
@ -271,7 +325,7 @@ Answer questions about this movie using only the verified database information a
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const systemPrompt = this.createSystemPrompt(context);
|
const systemPrompt = this.createSystemPrompt(context);
|
||||||
|
|
||||||
// Prepare messages for API
|
// Prepare messages for API
|
||||||
const messages = [
|
const messages = [
|
||||||
{ role: 'system', content: systemPrompt },
|
{ role: 'system', content: systemPrompt },
|
||||||
|
|
@ -288,7 +342,7 @@ Answer questions about this movie using only the verified database information a
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
console.log('[AIService] Sending request to OpenRouter with context:', {
|
console.log('[AIService] Sending request to OpenRouter with context:', {
|
||||||
contentType: 'showTitle' in context ? 'episode' : 'movie',
|
contentType: 'showTitle' in context ? 'episode' : 'movie',
|
||||||
title: 'showTitle' in context ?
|
title: 'showTitle' in context ?
|
||||||
`${(context as EpisodeContext).showTitle} S${(context as EpisodeContext).seasonNumber}E${(context as EpisodeContext).episodeNumber}` :
|
`${(context as EpisodeContext).showTitle} S${(context as EpisodeContext).seasonNumber}E${(context as EpisodeContext).episodeNumber}` :
|
||||||
(context as MovieContext).title,
|
(context as MovieContext).title,
|
||||||
messageCount: messages.length
|
messageCount: messages.length
|
||||||
|
|
@ -304,7 +358,7 @@ Answer questions about this movie using only the verified database information a
|
||||||
'X-Title': 'Nuvio - AI Chat',
|
'X-Title': 'Nuvio - AI Chat',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
model: 'openai/gpt-oss-20b:free',
|
model: 'xiaomi/mimo-v2-flash:free',
|
||||||
messages,
|
messages,
|
||||||
max_tokens: 1000,
|
max_tokens: 1000,
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
|
|
@ -321,13 +375,13 @@ Answer questions about this movie using only the verified database information a
|
||||||
}
|
}
|
||||||
|
|
||||||
const data: OpenRouterResponse = await response.json();
|
const data: OpenRouterResponse = await response.json();
|
||||||
|
|
||||||
if (!data.choices || data.choices.length === 0) {
|
if (!data.choices || data.choices.length === 0) {
|
||||||
throw new Error('No response received from AI service');
|
throw new Error('No response received from AI service');
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseContent = data.choices[0].message.content;
|
const responseContent = data.choices[0].message.content;
|
||||||
|
|
||||||
if (__DEV__ && data.usage) {
|
if (__DEV__ && data.usage) {
|
||||||
console.log('[AIService] Token usage:', data.usage);
|
console.log('[AIService] Token usage:', data.usage);
|
||||||
}
|
}
|
||||||
|
|
@ -368,7 +422,7 @@ Answer questions about this movie using only the verified database information a
|
||||||
// TMDB returns full ISO timestamps; keep only date part
|
// TMDB returns full ISO timestamps; keep only date part
|
||||||
releaseDate = String(anyDate).split('T')[0];
|
releaseDate = String(anyDate).split('T')[0];
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch { }
|
||||||
const statusText: string = (movieData.status || '').toString().toLowerCase();
|
const statusText: string = (movieData.status || '').toString().toLowerCase();
|
||||||
let released = statusText === 'released';
|
let released = statusText === 'released';
|
||||||
if (!released && releaseDate) {
|
if (!released && releaseDate) {
|
||||||
|
|
@ -408,16 +462,36 @@ Answer questions about this movie using only the verified database information a
|
||||||
})) || [],
|
})) || [],
|
||||||
runtime: movieData.runtime,
|
runtime: movieData.runtime,
|
||||||
tagline: movieData.tagline,
|
tagline: movieData.tagline,
|
||||||
keywords: movieData.keywords?.keywords?.map((k: any) => k.name) ||
|
keywords: movieData.keywords?.keywords?.map((k: any) => k.name) ||
|
||||||
movieData.keywords?.results?.map((k: any) => k.name) || []
|
movieData.keywords?.results?.map((k: any) => k.name) || [],
|
||||||
|
// Enhanced fields
|
||||||
|
voteAverage: movieData.vote_average,
|
||||||
|
voteCount: movieData.vote_count,
|
||||||
|
popularity: movieData.popularity,
|
||||||
|
budget: movieData.budget,
|
||||||
|
revenue: movieData.revenue,
|
||||||
|
productionCompanies: movieData.production_companies?.map((c: any) => c.name) || [],
|
||||||
|
productionCountries: movieData.production_countries?.map((c: any) => c.name) || [],
|
||||||
|
spokenLanguages: movieData.spoken_languages?.map((l: any) => l.english_name || l.name) || [],
|
||||||
|
originalLanguage: movieData.original_language,
|
||||||
|
status: movieData.status,
|
||||||
|
contentRating: (() => {
|
||||||
|
// Extract US content rating from release_dates
|
||||||
|
try {
|
||||||
|
const usRelease = movieData.release_dates?.results?.find((r: any) => r.iso_3166_1 === 'US');
|
||||||
|
const certification = usRelease?.release_dates?.find((d: any) => d.certification)?.certification;
|
||||||
|
return certification || undefined;
|
||||||
|
} catch { return undefined; }
|
||||||
|
})(),
|
||||||
|
imdbId: movieData.external_ids?.imdb_id || movieData.imdb_id,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper method to create context from TMDB episode data
|
// Helper method to create context from TMDB episode data
|
||||||
static createEpisodeContext(
|
static createEpisodeContext(
|
||||||
episodeData: any,
|
episodeData: any,
|
||||||
showData: any,
|
showData: any,
|
||||||
seasonNumber: number,
|
seasonNumber: number,
|
||||||
episodeNumber: number
|
episodeNumber: number
|
||||||
): EpisodeContext {
|
): EpisodeContext {
|
||||||
// Compute release status from TMDB air date
|
// Compute release status from TMDB air date
|
||||||
|
|
@ -428,7 +502,7 @@ Answer questions about this movie using only the verified database information a
|
||||||
const parsed = new Date(airDate);
|
const parsed = new Date(airDate);
|
||||||
if (!isNaN(parsed.getTime())) released = parsed.getTime() <= Date.now();
|
if (!isNaN(parsed.getTime())) released = parsed.getTime() <= Date.now();
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch { }
|
||||||
// Heuristics: if TMDB provides meaningful content, treat as released
|
// Heuristics: if TMDB provides meaningful content, treat as released
|
||||||
if (!released) {
|
if (!released) {
|
||||||
const hasOverview = typeof episodeData.overview === 'string' && episodeData.overview.trim().length > 40;
|
const hasOverview = typeof episodeData.overview === 'string' && episodeData.overview.trim().length > 40;
|
||||||
|
|
@ -479,7 +553,19 @@ Answer questions about this movie using only the verified database information a
|
||||||
guestStars: episodeData.credits?.guest_stars?.map((g: any) => ({
|
guestStars: episodeData.credits?.guest_stars?.map((g: any) => ({
|
||||||
name: g.name,
|
name: g.name,
|
||||||
character: g.character
|
character: g.character
|
||||||
})) || []
|
})) || [],
|
||||||
|
// Enhanced fields
|
||||||
|
voteAverage: episodeData.vote_average,
|
||||||
|
showGenres: showData.genres?.map((g: any) => g.name) || [],
|
||||||
|
showNetworks: showData.networks?.map((n: any) => n.name) || [],
|
||||||
|
showStatus: showData.status,
|
||||||
|
contentRating: (() => {
|
||||||
|
// Extract US content rating from show's content_ratings
|
||||||
|
try {
|
||||||
|
const usRating = showData.content_ratings?.results?.find((r: any) => r.iso_3166_1 === 'US');
|
||||||
|
return usRating?.rating || undefined;
|
||||||
|
} catch { return undefined; }
|
||||||
|
})(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -507,7 +593,7 @@ Answer questions about this movie using only the verified database information a
|
||||||
const parsed = new Date(airDate);
|
const parsed = new Date(airDate);
|
||||||
if (!isNaN(parsed.getTime())) released = parsed.getTime() <= Date.now();
|
if (!isNaN(parsed.getTime())) released = parsed.getTime() <= Date.now();
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch { }
|
||||||
if (!released) {
|
if (!released) {
|
||||||
const hasOverview = typeof ep.overview === 'string' && ep.overview.trim().length > 40;
|
const hasOverview = typeof ep.overview === 'string' && ep.overview.trim().length > 40;
|
||||||
const hasRuntime = typeof ep.runtime === 'number' && ep.runtime > 0;
|
const hasRuntime = typeof ep.runtime === 'number' && ep.runtime > 0;
|
||||||
|
|
@ -520,7 +606,8 @@ Answer questions about this movie using only the verified database information a
|
||||||
title: ep.name || `Episode ${ep.episode_number}`,
|
title: ep.name || `Episode ${ep.episode_number}`,
|
||||||
airDate,
|
airDate,
|
||||||
released,
|
released,
|
||||||
overview: ep.overview || ''
|
overview: ep.overview || '',
|
||||||
|
voteAverage: ep.vote_average,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -542,6 +629,23 @@ Answer questions about this movie using only the verified database information a
|
||||||
cast,
|
cast,
|
||||||
crew,
|
crew,
|
||||||
episodesBySeason: normalized,
|
episodesBySeason: normalized,
|
||||||
|
// Enhanced fields
|
||||||
|
networks: showData.networks?.map((n: any) => n.name) || [],
|
||||||
|
status: showData.status,
|
||||||
|
originalLanguage: showData.original_language,
|
||||||
|
popularity: showData.popularity,
|
||||||
|
voteAverage: showData.vote_average,
|
||||||
|
voteCount: showData.vote_count,
|
||||||
|
createdBy: showData.created_by?.map((c: any) => c.name) || [],
|
||||||
|
contentRating: (() => {
|
||||||
|
// Extract US content rating
|
||||||
|
try {
|
||||||
|
const usRating = showData.content_ratings?.results?.find((r: any) => r.iso_3166_1 === 'US');
|
||||||
|
return usRating?.rating || undefined;
|
||||||
|
} catch { return undefined; }
|
||||||
|
})(),
|
||||||
|
productionCompanies: showData.production_companies?.map((c: any) => c.name) || [],
|
||||||
|
type: showData.type,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue