Rthur2003 commited on
Commit
61ddcc8
·
1 Parent(s): eb80da4

refactor: Optimize YouTube comment generation and improve error handling

Browse files
Files changed (1) hide show
  1. app/routes/commend/gemini_service.py +87 -98
app/routes/commend/gemini_service.py CHANGED
@@ -1,7 +1,6 @@
1
  """
2
  Gemini AI Service for Crown Commend
3
- Generates contextual YouTube comments using Google's Gemini API.
4
- Uses the new google.genai package (replacing deprecated google.generativeai).
5
  """
6
 
7
  from __future__ import annotations
@@ -10,13 +9,11 @@ import os
10
  from typing import Optional, Tuple, List, Dict, Any
11
 
12
  from google import genai
13
- from google.genai import types
14
 
15
  from ...services.logging_config import get_logger
16
 
17
  logger = get_logger(__name__)
18
 
19
- # Gemini client configuration
20
  _client = None
21
 
22
 
@@ -29,42 +26,58 @@ def _get_client():
29
 
30
  api_key = os.getenv('COMMEND_GEMINI_API_KEY')
31
  if not api_key:
32
- raise ValueError("COMMEND_GEMINI_API_KEY environment variable is required")
33
 
34
  try:
35
  _client = genai.Client(api_key=api_key)
36
- logger.info("Gemini client initialized successfully")
37
  return _client
38
  except Exception as e:
39
- logger.error(f"Failed to initialize Gemini: {e}")
40
- raise ValueError(f"Gemini initialization failed: {e}")
41
 
42
 
43
  def summarize_transcript(transcript_text: str, language: str) -> Tuple[Optional[str], Optional[str]]:
44
- """Video transcript'ini özetler."""
45
  try:
46
  client = _get_client()
47
 
48
  if not transcript_text or len(transcript_text.strip()) < 50:
49
- return None, "Insufficient transcript data"
50
 
51
- prompt = f"""
52
- Bu video transcript'ini {language} dilinde 2-3 cümlelik kısa bir özet haline getir:
53
 
54
- {transcript_text}
55
-
56
- ONEMLI: Sadece özetin kendisini yaz, başka açıklama yapma.
57
- """
58
 
59
  response = client.models.generate_content(
60
  model="gemini-2.0-flash",
61
  contents=prompt
62
  )
63
- return response.text.strip(), None
64
 
65
  except Exception as e:
66
- logger.error(f"Transcript summarization error: {e}")
67
- return None, "Failed to summarize transcript"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
 
70
  def generate_comment(
@@ -75,99 +88,75 @@ def generate_comment(
75
  transcript_summary: Optional[str] = None
76
  ) -> Tuple[Optional[str], Optional[str]]:
77
  """
78
- AI-powered comment generation using Gemini.
79
-
80
- Args:
81
- video_details: Video metadata (title, channel, stats, etc.)
82
- comment_style: Style preference (supportive, analytical, humorous, etc.)
83
- language: Target language for the comment
84
- existing_comments: Sample comments from the video
85
- transcript_summary: Optional transcript summary
86
-
87
- Returns:
88
- (generated_comment, error_message)
89
  """
90
  try:
91
  client = _get_client()
92
 
93
- # Format existing comments
94
- comment_section = "No comments available to analyze."
95
- if existing_comments:
96
- comment_section = "\n".join(
97
- [f"- '{c['text'][:150]}' (by {c['author']})" for c in existing_comments[:5]]
98
- )
99
-
100
- # Build the prompt
101
- prompt = f"""
102
- ### GOREV ###
103
- Sen Crown Commend AI'sin - YouTube videolarına özgün, yaratıcı ve değerli yorumlar üreten bir yapay zeka.
104
- Yorum dili: **{language}**
105
-
106
- ### VIDEO ANALIZI ###
107
-
108
- **Mevcut Yorumlar (ONCELIKLI):**
109
- {comment_section}
110
-
111
- **Video Icerigi:**
112
- {f"- Ozet: {transcript_summary}" if transcript_summary else "- Icerik ozeti mevcut degil"}
113
-
114
- **Video Bilgileri:**
115
- - Baslik: {video_details.get('title', 'Bilinmiyor')}
116
- - Kanal: {video_details.get('channelName', 'Bilinmiyor')}
117
- - Aciklama: {video_details.get('description', '')[:300]}
118
-
119
- **Istatistikler:**
120
- - Izlenme: {video_details.get('viewCount', 0):,}
121
- - Begeni: {video_details.get('likeCount', 0):,}
122
- - Sure: {video_details.get('duration', 0) // 60} dakika
123
-
124
- ### YORUM STRATEJISI ###
125
- **{comment_style}** stilinde yorum yaz:
126
-
127
- 1. **Gercekcilik:** Normal bir izleyici gibi yaz, abartma.
128
- 2. **Emoji Siniri:** Hic emoji kullanma veya en fazla 1 adet kullan.
129
- 3. **Dogallik:** Samimi ama sade bir dil kullan.
130
- 4. **Video Odak:** Videonun icerigini ve mevcut yorumlari on planda tut.
131
- 5. **Kisalik:** Uzun yorumlardan kacin, net ve oz ol.
132
-
133
- ### YORUM KURALLARI ###
134
- - **Dil:** Tamamen **{language}** dilinde yaz (disclaimer dahil)
135
- - **Uzunluk:** 1-3 cumle arasi, kisa ve etkili
136
- - **Ton:** Video konusuna uygun (ciddi/eglenceli/destekleyici)
137
- - **Ozgunluk:** Klise ifadelerden kacin
138
- - **Disclaimer:** Yorumun sonuna yeni satirda disclaimer ekle:
139
- - Turkish: "Not: Bu yorum AI tarafindan uretilmistir."
140
- - English: "Note: This comment was generated by AI."
141
- - Russian: "Primechanie: Etot kommentariy sozdan II."
142
- - Chinese: "Zhu: Ci pinglun you AI shengcheng."
143
- - Japanese: "Chu: Kono komento wa AI ni yotte seisei saremashita."
144
-
145
- ### ORNEKLER ###
146
- Kotu: "Harika video! Cok begendim!"
147
- Iyi: "3:45'teki o detay gercekten onemli bir noktaydi, bu konuya hic bu acidan bakmamistim."
148
-
149
- **Yorumun:**
150
- """
151
 
152
  response = client.models.generate_content(
153
  model="gemini-2.0-flash",
154
  contents=prompt
155
  )
156
- generated_text = response.text.strip()
157
 
158
- # Clean up the response if needed
159
- if generated_text.startswith('"') and generated_text.endswith('"'):
160
- generated_text = generated_text[1:-1]
 
 
 
 
 
 
161
 
162
- return generated_text, None
163
 
164
  except Exception as e:
165
- logger.error(f"Comment generation error: {e}")
166
  error_str = str(e).lower()
167
 
168
  if "api key" in error_str:
169
- return None, "Invalid API key configuration"
170
- elif "quota" in error_str or "limit" in error_str:
171
- return None, "API quota exceeded, please try again later"
172
  else:
173
- return None, f"Failed to generate comment: {str(e)}"
 
1
  """
2
  Gemini AI Service for Crown Commend
3
+ Token-optimized YouTube comment generation.
 
4
  """
5
 
6
  from __future__ import annotations
 
9
  from typing import Optional, Tuple, List, Dict, Any
10
 
11
  from google import genai
 
12
 
13
  from ...services.logging_config import get_logger
14
 
15
  logger = get_logger(__name__)
16
 
 
17
  _client = None
18
 
19
 
 
26
 
27
  api_key = os.getenv('COMMEND_GEMINI_API_KEY')
28
  if not api_key:
29
+ raise ValueError("COMMEND_GEMINI_API_KEY required")
30
 
31
  try:
32
  _client = genai.Client(api_key=api_key)
33
+ logger.info("Gemini initialized")
34
  return _client
35
  except Exception as e:
36
+ logger.error(f"Gemini init failed: {e}")
37
+ raise ValueError(f"Gemini failed: {e}")
38
 
39
 
40
  def summarize_transcript(transcript_text: str, language: str) -> Tuple[Optional[str], Optional[str]]:
41
+ """Transcript özeti - max 2000 karakter input."""
42
  try:
43
  client = _get_client()
44
 
45
  if not transcript_text or len(transcript_text.strip()) < 50:
46
+ return None, "No transcript"
47
 
48
+ # Token tasarrufu: max 2000 karakter al
49
+ text = transcript_text[:2000]
50
 
51
+ prompt = f"Summarize in {language}, 1-2 sentences only:\n{text}"
 
 
 
52
 
53
  response = client.models.generate_content(
54
  model="gemini-2.0-flash",
55
  contents=prompt
56
  )
57
+ return response.text.strip()[:500], None
58
 
59
  except Exception as e:
60
+ logger.error(f"Summary error: {e}")
61
+ return None, "Summary failed"
62
+
63
+
64
+ # Disclaimer mapping - sabit, prompt'a dahil etmiyoruz
65
+ DISCLAIMERS = {
66
+ 'Turkish': '\n\nNot: Bu yorum AI tarafından üretilmiştir.',
67
+ 'English': '\n\nNote: This comment was generated by AI.',
68
+ 'Russian': '\n\nПримечание: Этот комментарий создан ИИ.',
69
+ 'Chinese': '\n\n注:此评论由AI生成。',
70
+ 'Japanese': '\n\n注:このコメントはAIによって生成されました。'
71
+ }
72
+
73
+ # Style mapping - kısa açıklamalar
74
+ STYLES = {
75
+ 'supportive': 'positive, encouraging',
76
+ 'analytical': 'thoughtful, detailed',
77
+ 'humorous': 'witty, fun',
78
+ 'curious': 'asking questions',
79
+ 'professional': 'formal, respectful'
80
+ }
81
 
82
 
83
  def generate_comment(
 
88
  transcript_summary: Optional[str] = None
89
  ) -> Tuple[Optional[str], Optional[str]]:
90
  """
91
+ Token-optimized comment generation.
92
+ ~300-400 tokens input hedefi.
 
 
 
 
 
 
 
 
 
93
  """
94
  try:
95
  client = _get_client()
96
 
97
+ # Kısa video bilgisi
98
+ title = video_details.get('title', '')[:80]
99
+ channel = video_details.get('channelName', '')[:30]
100
+ desc = video_details.get('description', '')[:150]
101
+ views = video_details.get('viewCount', 0)
102
+
103
+ # Sadece en iyi 2 yorum, kısa
104
+ comments_text = ""
105
+ if existing_comments and len(existing_comments) > 0:
106
+ top_comments = existing_comments[:2]
107
+ comments_text = " | ".join([c['text'][:80] for c in top_comments])
108
+
109
+ # Özet varsa ekle (kısa)
110
+ summary_text = ""
111
+ if transcript_summary:
112
+ summary_text = f"Content: {transcript_summary[:200]}"
113
+
114
+ # Stil açıklaması
115
+ style_desc = STYLES.get(comment_style, 'natural')
116
+
117
+ # Kompakt prompt (~300 token)
118
+ prompt = f"""Write a YouTube comment in {language}.
119
+
120
+ Video: {title}
121
+ Channel: {channel}
122
+ {f'Desc: {desc}' if desc else ''}
123
+ {f'Views: {views:,}' if views else ''}
124
+ {f'Comments: {comments_text}' if comments_text else ''}
125
+ {summary_text}
126
+
127
+ Style: {style_desc}
128
+ Rules:
129
+ - 1-2 sentences max
130
+ - Sound natural, like a real viewer
131
+ - No emojis or minimal (max 1)
132
+ - Be specific to video content
133
+
134
+ Comment:"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
 
136
  response = client.models.generate_content(
137
  model="gemini-2.0-flash",
138
  contents=prompt
139
  )
 
140
 
141
+ generated = response.text.strip()
142
+
143
+ # Tırnak temizle
144
+ if generated.startswith('"') and generated.endswith('"'):
145
+ generated = generated[1:-1]
146
+
147
+ # Disclaimer ekle
148
+ disclaimer = DISCLAIMERS.get(language, DISCLAIMERS['English'])
149
+ final_comment = generated + disclaimer
150
 
151
+ return final_comment, None
152
 
153
  except Exception as e:
154
+ logger.error(f"Generate error: {e}")
155
  error_str = str(e).lower()
156
 
157
  if "api key" in error_str:
158
+ return None, "Invalid API key"
159
+ elif "quota" in error_str or "limit" in error_str or "resource" in error_str:
160
+ return None, "API quota exceeded"
161
  else:
162
+ return None, f"Generation failed: {str(e)}"