""" OpenAI-compatible API Client for company profile generation """ import time from typing import Dict, List, Optional import openai class CompanyProfileClient: def __init__(self, api_key: str, base_url: str, model: str = "gemini-1.5-flash"): """Initialize OpenAI client with API key, base_url and model""" self.client = openai.AsyncOpenAI(api_key=api_key, base_url=base_url) self.model_name = model async def generate_profile( self, company_name: str, ts_code: str, financial_data: Optional[Dict] = None ) -> Dict: """ Generate company profile using OpenAI-compatible API (non-streaming) Args: company_name: Company name ts_code: Stock code financial_data: Optional financial data for context Returns: Dict with profile content and metadata """ start_time = time.perf_counter_ns() # Build prompt prompt = self._build_prompt(company_name, ts_code, financial_data) # Call OpenAI-compatible API try: response = await self.client.chat.completions.create( model=self.model_name, messages=[{"role": "user", "content": prompt}], ) content = response.choices[0].message.content if response.choices else "" usage = response.usage elapsed_ms = int((time.perf_counter_ns() - start_time) / 1_000_000) return { "content": content, "model": self.model_name, "tokens": { "prompt_tokens": usage.prompt_tokens if usage else 0, "completion_tokens": usage.completion_tokens if usage else 0, "total_tokens": usage.total_tokens if usage else 0, } if usage else {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0}, "elapsed_ms": elapsed_ms, "success": True, } except Exception as e: elapsed_ms = int((time.perf_counter_ns() - start_time) / 1_000_000) return { "content": "", "model": self.model_name, "tokens": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0}, "elapsed_ms": elapsed_ms, "success": False, "error": str(e), } def generate_profile_stream( self, company_name: str, ts_code: str, financial_data: Optional[Dict] = None ): """ Generate company profile using Gemini API with streaming Args: company_name: Company name ts_code: Stock code financial_data: Optional financial data for context Yields: Chunks of generated content """ import logging logger = logging.getLogger(__name__) logger.info(f"[CompanyProfile] Starting stream generation for {company_name} ({ts_code})") # Build prompt prompt = self._build_prompt(company_name, ts_code, financial_data) logger.info(f"[CompanyProfile] Prompt built, length: {len(prompt)} chars") # Call Gemini API with streaming try: logger.info("[CompanyProfile] Calling Gemini API with stream=True") # Generate streaming response (sync call, but yields chunks) response_stream = self.model.generate_content(prompt, stream=True) logger.info("[CompanyProfile] Gemini API stream object created") chunk_count = 0 # Stream chunks logger.info("[CompanyProfile] Starting to iterate response stream") for chunk in response_stream: logger.info(f"[CompanyProfile] Received chunk from Gemini, has text: {hasattr(chunk, 'text')}") if hasattr(chunk, 'text') and chunk.text: chunk_count += 1 text_len = len(chunk.text) logger.info(f"[CompanyProfile] Chunk {chunk_count}: {text_len} chars") yield chunk.text else: logger.warning(f"[CompanyProfile] Chunk has no text attribute or empty, chunk: {chunk}") logger.info(f"[CompanyProfile] Stream iteration completed. Total chunks: {chunk_count}") except Exception as e: logger.error(f"[CompanyProfile] Error during streaming: {type(e).__name__}: {str(e)}", exc_info=True) yield f"\n\n---\n\n**错误**: {type(e).__name__}: {str(e)}" def _build_prompt(self, company_name: str, ts_code: str, financial_data: Optional[Dict] = None) -> str: """Build prompt for company profile generation""" prompt = f"""您是一位专业的证券市场分析师。请为公司 {company_name} (股票代码: {ts_code}) 生成一份详细且专业的公司介绍。开头不要自我介绍,直接开始正文。正文用MarkDown输出,尽量说明信息来源,用斜体显示信息来源。在生成内容时,请严格遵循以下要求并采用清晰、结构化的格式: 1. **公司概览**: * 简要介绍公司的性质、核心业务领域及其在行业中的定位。 * 提炼并阐述公司的核心价值理念。 2. **主营业务**: * 详细描述公司主要的**产品或服务**。 * **重要提示**:如果能获取到公司最新的官方**年报**或**财务报告**,请从中提取各主要产品/服务线的**收入金额**和其占公司总收入的**百分比**。请**明确标注数据来源**(例如:"数据来源于XX年年度报告")。 * **严格禁止**编造或估算任何财务数据。若无法找到公开、准确的财务数据,请**不要**在这一点中提及具体金额或比例,仅描述业务内容。 3. **发展历程**: * 以时间线或关键事件的形式,概述公司自成立以来的主要**里程碑事件**、重大发展阶段、战略转型或重要成就。 4. **核心团队**: * 介绍公司**主要管理层和核心技术团队成员**。 * 对于每位核心成员,提供其**职务、主要工作履历、教育背景**。 * 如果公开可查,可补充其**出生年份**。 5. **供应链**: * 描述公司的**主要原材料、部件或服务来源**。 * 如果公开信息中包含,请列出**主要供应商名称**,并**明确其在总采购金额中的大致占比**。若无此数据,则仅描述采购模式。 6. **主要客户及销售模式**: * 阐明公司的**销售模式**(例如:直销、经销、线上销售、代理等)。 * 列出公司的**主要客户群体**或**代表性大客户**。 * 如果公开信息中包含,请标明**主要客户(或前五大客户)的销售额占公司总销售额的比例**。若无此数据,则仅描述客户类型。 7. **未来展望**: * 基于公司**公开的官方声明、管理层访谈或战略规划**,总结公司未来的发展方向、战略目标、重点项目或市场预期。请确保此部分内容有可靠的信息来源支持。""" if financial_data: prompt += f"\n\n参考财务数据:\n{financial_data}" return prompt