211 lines
5.7 KiB
Go
211 lines
5.7 KiB
Go
package ai
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
|
|
"dealroom/internal/model"
|
|
)
|
|
|
|
// K25Client handles communication with K2.5 AI service
|
|
type K25Client struct {
|
|
baseURL string
|
|
apiKey string
|
|
client *http.Client
|
|
}
|
|
|
|
// NewK25Client creates a new K2.5 client
|
|
func NewK25Client(baseURL, apiKey string) *K25Client {
|
|
return &K25Client{
|
|
baseURL: baseURL,
|
|
apiKey: apiKey,
|
|
client: &http.Client{
|
|
Timeout: 30 * time.Second,
|
|
},
|
|
}
|
|
}
|
|
|
|
// AnalysisRequest represents a request for document analysis
|
|
type AnalysisRequest struct {
|
|
Text string `json:"text"`
|
|
Category string `json:"category,omitempty"` // nda, cim, financial_model, etc.
|
|
Language string `json:"language,omitempty"`
|
|
}
|
|
|
|
// AnalysisResponse represents the response from K2.5 analysis
|
|
type AnalysisResponse struct {
|
|
Summary string `json:"summary"`
|
|
KeyMetrics []string `json:"key_metrics"`
|
|
RiskFactors []string `json:"risk_factors"`
|
|
Category string `json:"category"`
|
|
Confidence float64 `json:"confidence"`
|
|
ProcessingMs int64 `json:"processing_ms"`
|
|
}
|
|
|
|
// EmbeddingRequest represents a request for text embeddings
|
|
type EmbeddingRequest struct {
|
|
Text string `json:"text"`
|
|
Model string `json:"model,omitempty"`
|
|
}
|
|
|
|
// EmbeddingResponse represents the response from K2.5 embedding
|
|
type EmbeddingResponse struct {
|
|
Embedding []float64 `json:"embedding"`
|
|
Dimension int `json:"dimension"`
|
|
Model string `json:"model"`
|
|
}
|
|
|
|
// AnalyzeDocument sends text to K2.5 for analysis
|
|
func (c *K25Client) AnalyzeDocument(text, category string) (*model.DocumentAnalysis, error) {
|
|
if c.baseURL == "" || c.apiKey == "" {
|
|
return nil, fmt.Errorf("K2.5 client not configured")
|
|
}
|
|
|
|
req := AnalysisRequest{
|
|
Text: text,
|
|
Category: category,
|
|
Language: "en",
|
|
}
|
|
|
|
jsonData, err := json.Marshal(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal request: %w", err)
|
|
}
|
|
|
|
httpReq, err := http.NewRequest("POST", c.baseURL+"/analyze", bytes.NewBuffer(jsonData))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
|
|
httpReq.Header.Set("Content-Type", "application/json")
|
|
httpReq.Header.Set("Authorization", "Bearer "+c.apiKey)
|
|
|
|
resp, err := c.client.Do(httpReq)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to send request: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return nil, fmt.Errorf("K2.5 API error %d: %s", resp.StatusCode, string(body))
|
|
}
|
|
|
|
var analysisResp AnalysisResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&analysisResp); err != nil {
|
|
return nil, fmt.Errorf("failed to decode response: %w", err)
|
|
}
|
|
|
|
return &model.DocumentAnalysis{
|
|
Summary: analysisResp.Summary,
|
|
KeyMetrics: analysisResp.KeyMetrics,
|
|
RiskFactors: analysisResp.RiskFactors,
|
|
AIConfidence: analysisResp.Confidence,
|
|
}, nil
|
|
}
|
|
|
|
// GenerateEmbedding creates vector embeddings for text
|
|
func (c *K25Client) GenerateEmbedding(text string) ([]float64, error) {
|
|
if c.baseURL == "" || c.apiKey == "" {
|
|
return nil, fmt.Errorf("K2.5 client not configured")
|
|
}
|
|
|
|
req := EmbeddingRequest{
|
|
Text: text,
|
|
Model: "default",
|
|
}
|
|
|
|
jsonData, err := json.Marshal(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal request: %w", err)
|
|
}
|
|
|
|
httpReq, err := http.NewRequest("POST", c.baseURL+"/embed", bytes.NewBuffer(jsonData))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
|
|
httpReq.Header.Set("Content-Type", "application/json")
|
|
httpReq.Header.Set("Authorization", "Bearer "+c.apiKey)
|
|
|
|
resp, err := c.client.Do(httpReq)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to send request: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return nil, fmt.Errorf("K2.5 API error %d: %s", resp.StatusCode, string(body))
|
|
}
|
|
|
|
var embeddingResp EmbeddingResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&embeddingResp); err != nil {
|
|
return nil, fmt.Errorf("failed to decode response: %w", err)
|
|
}
|
|
|
|
return embeddingResp.Embedding, nil
|
|
}
|
|
|
|
// SearchSimilar finds documents similar to the given text
|
|
func (c *K25Client) SearchSimilar(text string, limit int) ([]SimilarDocument, error) {
|
|
// This would typically involve:
|
|
// 1. Generate embedding for the search text
|
|
// 2. Query the database for similar embeddings using cosine similarity
|
|
// 3. Return ranked results
|
|
|
|
// For now, return a placeholder implementation
|
|
return []SimilarDocument{}, fmt.Errorf("not implemented")
|
|
}
|
|
|
|
// SimilarDocument represents a document similar to the search query
|
|
type SimilarDocument struct {
|
|
EntryID string `json:"entry_id"`
|
|
Title string `json:"title"`
|
|
Similarity float64 `json:"similarity"`
|
|
Snippet string `json:"snippet"`
|
|
}
|
|
|
|
// Health checks if the K2.5 service is available
|
|
func (c *K25Client) Health() error {
|
|
if c.baseURL == "" {
|
|
return fmt.Errorf("K2.5 client not configured")
|
|
}
|
|
|
|
req, err := http.NewRequest("GET", c.baseURL+"/health", nil)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create health check request: %w", err)
|
|
}
|
|
|
|
if c.apiKey != "" {
|
|
req.Header.Set("Authorization", "Bearer "+c.apiKey)
|
|
}
|
|
|
|
resp, err := c.client.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("health check failed: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("K2.5 service unhealthy: status %d", resp.StatusCode)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ExtractTextFromFile extracts text content from various file types
|
|
func ExtractTextFromFile(filePath, mimeType string) (string, error) {
|
|
// This would implement text extraction from:
|
|
// - PDF files
|
|
// - Microsoft Office documents (DOCX, XLSX, PPTX)
|
|
// - Plain text files
|
|
// - Images with OCR
|
|
|
|
// For now, return a placeholder
|
|
return "", fmt.Errorf("text extraction not implemented for %s", mimeType)
|
|
} |