//go:build llm_script package main import ( "fmt" "regexp" "strings" ) const ( defaultPerplexityPricingFetchURL = "https://docs.perplexity.ai/docs/agent-api/models.md" defaultPerplexityPricingSourceURL = "https://docs.perplexity.ai/docs/agent-api/models" ) var markdownLinkPattern = regexp.MustCompile(`\[(.*?)\]\((https://[^)]+)\)`) func parsePerplexityPricingCatalog(raw string) ([]officialPricingRecord, error) { lines := strings.Split(raw, "\n") records := make([]officialPricingRecord, 0) header := []string(nil) modelIndex := -1 inputIndex := -1 outputIndex := -1 docIndex := -1 for _, line := range lines { line = strings.TrimSpace(line) if !strings.HasPrefix(line, "|") { continue } parts := splitMarkdownTableRow(line) if len(parts) == 0 { continue } if header == nil { header = parts modelIndex, inputIndex, outputIndex, docIndex = detectPerplexityTableColumns(parts) continue } if isMarkdownTableSeparator(parts) { continue } if modelIndex < 0 || inputIndex < 0 || outputIndex < 0 || modelIndex >= len(parts) || inputIndex >= len(parts) || outputIndex >= len(parts) { continue } modelPath := strings.Trim(parts[modelIndex], "`") inputCell := parts[inputIndex] outputCell := parts[outputIndex] inputPrice, ok := firstDollarPrice(inputCell) if !ok { continue } outputPrice, ok := firstDollarPrice(outputCell) if !ok { continue } sourceURL := defaultPerplexityPricingSourceURL if docIndex >= 0 && docIndex < len(parts) { if matches := markdownLinkPattern.FindStringSubmatch(parts[docIndex]); len(matches) == 3 { sourceURL = matches[2] } } providerName := providerFromModelPath(modelPath) providerNameCn, providerCountry, providerWebsite := providerMetadata(providerName) record := officialPricingRecord{ ModelID: normalizeExternalID("perplexity", modelPath), ModelName: modelPath, ProviderName: providerName, ProviderNameCn: providerNameCn, ProviderCountry: providerCountry, ProviderWebsite: providerWebsite, OperatorName: "Perplexity API", OperatorNameCn: "Perplexity API", OperatorCountry: "US", OperatorWebsite: "https://docs.perplexity.ai", OperatorType: "relay", Region: "global", Currency: "USD", InputPrice: inputPrice, OutputPrice: outputPrice, SourceURL: defaultPerplexityPricingSourceURL, ModelSourceURL: sourceURL, DateConfidence: "unknown", DateSourceKind: "official_pricing", Modality: detectModality(modelPath), } record.IsFree = record.InputPrice == 0 && record.OutputPrice == 0 records = append(records, record) } if len(records) == 0 { return nil, fmt.Errorf("unexpected perplexity pricing content") } return records, nil } func splitMarkdownTableRow(line string) []string { trimmed := strings.TrimSpace(line) trimmed = strings.TrimPrefix(trimmed, "|") trimmed = strings.TrimSuffix(trimmed, "|") if trimmed == "" { return nil } parts := strings.Split(trimmed, "|") result := make([]string, 0, len(parts)) for _, part := range parts { result = append(result, strings.TrimSpace(part)) } return result } func detectPerplexityTableColumns(header []string) (int, int, int, int) { modelIndex := -1 inputIndex := -1 outputIndex := -1 docIndex := -1 for i, col := range header { lower := strings.ToLower(strings.TrimSpace(col)) switch { case strings.Contains(lower, "model") && modelIndex == -1: modelIndex = i case strings.Contains(lower, "input") && (strings.Contains(lower, "price") || strings.Contains(lower, "$") || strings.Contains(lower, "/1m")) && inputIndex == -1: inputIndex = i case strings.Contains(lower, "output") && (strings.Contains(lower, "price") || strings.Contains(lower, "$") || strings.Contains(lower, "/1m")) && outputIndex == -1: outputIndex = i case (strings.Contains(lower, "documentation") || strings.Contains(lower, "docs")) && docIndex == -1: docIndex = i } } return modelIndex, inputIndex, outputIndex, docIndex } func isMarkdownTableSeparator(parts []string) bool { if len(parts) == 0 { return false } for _, part := range parts { trimmed := strings.TrimSpace(part) if trimmed == "" { return false } for _, ch := range trimmed { if ch != '-' && ch != ':' { return false } } } return true }