Files
llm-intelligence/scripts/ctyun_subscription_lib.go

225 lines
7.5 KiB
Go
Raw Normal View History

//go:build llm_script
package main
import (
"fmt"
"regexp"
"strings"
)
const (
defaultCTYunCodingPlanURL = "https://www.ctyun.cn/document/11061839/11092368"
defaultCTYunTokenPlanURL = "https://www.ctyun.cn/act/AI/zhuanxiang"
)
func parseCTYunSubscriptionCatalog(codingRaw string, tokenRaw string) ([]subscriptionImportRecord, error) {
publishedAt, known := publishedAtFromText(firstNonEmptyText(codingRaw, tokenRaw))
codingRecords, err := parseCTYunCodingPlan(codingRaw, publishedAt)
if err != nil {
return nil, err
}
tokenRecords, err := parseCTYunTokenPlan(tokenRaw, publishedAt)
if err != nil {
return nil, err
}
records := append(codingRecords, tokenRecords...)
for i := range records {
records[i].PublishedAtKnown = known
}
return records, nil
}
func parseCTYunCodingPlan(raw string, publishedAt string) ([]subscriptionImportRecord, error) {
if !strings.Contains(raw, "GLM Lite") || !strings.Contains(raw, "GLM Max") {
return nil, fmt.Errorf("ctyun coding plan tiers not found")
}
pricePattern := regexp.MustCompile(`包月价格\s+(\d+)元/月\s+(\d+)元/月\s+(\d+)元/月`)
priceMatch := pricePattern.FindStringSubmatch(raw)
if len(priceMatch) != 4 {
return nil, fmt.Errorf("ctyun coding plan monthly prices not found")
}
limitPattern := regexp.MustCompile(`每月最多约([\d,]+)次prompts`)
limitMatches := limitPattern.FindAllStringSubmatch(raw, -1)
if len(limitMatches) < 3 {
return nil, fmt.Errorf("ctyun coding plan monthly limits not found")
}
modelScope := extractCTYunCodingModels(raw)
records := []subscriptionImportRecord{
{
ProviderName: "Telecom",
ProviderNameCn: "中国电信",
ProviderCountry: "CN",
ProviderWebsite: "https://www.ctyun.cn",
OperatorName: "CTYun",
OperatorNameCn: "天翼云",
OperatorCountry: "CN",
OperatorWebsite: "https://www.ctyun.cn",
OperatorType: "cloud",
PlanFamily: "coding_plan",
PlanCode: "ctyun-coding-plan-lite-monthly",
PlanName: "天翼云 Coding Plan Lite月付",
Tier: "Lite",
BillingCycle: "monthly",
Currency: "CNY",
ListPrice: mustParseSubscriptionPrice(priceMatch[1]),
PriceUnit: "CNY/month",
QuotaValue: mustParseSubscriptionInt64(limitMatches[0][1]),
QuotaUnit: "prompts/month",
PlanScope: "Coding Plan",
ModelScope: modelScope,
SourceURL: defaultCTYunCodingPlanURL,
PublishedAt: publishedAt,
EffectiveDate: effectiveDateFromPublishedAt(publishedAt),
Notes: "每 5 小时约 80 次 prompts每周约 400 次 prompts。",
},
{
ProviderName: "Telecom",
ProviderNameCn: "中国电信",
ProviderCountry: "CN",
ProviderWebsite: "https://www.ctyun.cn",
OperatorName: "CTYun",
OperatorNameCn: "天翼云",
OperatorCountry: "CN",
OperatorWebsite: "https://www.ctyun.cn",
OperatorType: "cloud",
PlanFamily: "coding_plan",
PlanCode: "ctyun-coding-plan-pro-monthly",
PlanName: "天翼云 Coding Plan Pro月付",
Tier: "Pro",
BillingCycle: "monthly",
Currency: "CNY",
ListPrice: mustParseSubscriptionPrice(priceMatch[2]),
PriceUnit: "CNY/month",
QuotaValue: mustParseSubscriptionInt64(limitMatches[1][1]),
QuotaUnit: "prompts/month",
PlanScope: "Coding Plan",
ModelScope: modelScope,
SourceURL: defaultCTYunCodingPlanURL,
PublishedAt: publishedAt,
EffectiveDate: effectiveDateFromPublishedAt(publishedAt),
Notes: "每 5 小时约 400 次 prompts每周约 2,000 次 prompts。",
},
{
ProviderName: "Telecom",
ProviderNameCn: "中国电信",
ProviderCountry: "CN",
ProviderWebsite: "https://www.ctyun.cn",
OperatorName: "CTYun",
OperatorNameCn: "天翼云",
OperatorCountry: "CN",
OperatorWebsite: "https://www.ctyun.cn",
OperatorType: "cloud",
PlanFamily: "coding_plan",
PlanCode: "ctyun-coding-plan-max-monthly",
PlanName: "天翼云 Coding Plan Max月付",
Tier: "Max",
BillingCycle: "monthly",
Currency: "CNY",
ListPrice: mustParseSubscriptionPrice(priceMatch[3]),
PriceUnit: "CNY/month",
QuotaValue: mustParseSubscriptionInt64(limitMatches[2][1]),
QuotaUnit: "prompts/month",
PlanScope: "Coding Plan",
ModelScope: modelScope,
SourceURL: defaultCTYunCodingPlanURL,
PublishedAt: publishedAt,
EffectiveDate: effectiveDateFromPublishedAt(publishedAt),
Notes: "每 5 小时约 1,600 次 prompts每周约 8,000 次 prompts。",
},
}
return records, nil
}
func parseCTYunTokenPlan(raw string, publishedAt string) ([]subscriptionImportRecord, error) {
pattern := regexp.MustCompile(`Token Plan ([^\n]+?)(\d+(?:\.\d+)?亿|\d+万)Tokens包[\s\S]*?支持模型:([^\n]+)[\s\S]*?(\d+\s*\.\s*\d+)\s*元/个`)
matches := pattern.FindAllStringSubmatch(raw, -1)
if len(matches) != 6 {
return nil, fmt.Errorf("unexpected ctyun token plan count: %d", len(matches))
}
codeByTier := map[string]string{
"Lite": "lite",
"Pro": "pro",
"Max": "max",
"轻享包": "starter",
"畅享包": "plus",
"尊享包": "vip",
}
records := make([]subscriptionImportRecord, 0, len(matches))
for _, match := range matches {
rawTier := strings.TrimSpace(match[1])
tierCode := codeByTier[rawTier]
quotaValue := parseChineseTokenQuota(match[2])
price := mustParseSubscriptionPrice(strings.ReplaceAll(match[4], " ", ""))
planName := "天翼云 Token Plan " + rawTier
if rawTier == "Lite" || rawTier == "Pro" || rawTier == "Max" {
planName = "天翼云 Token Plan " + rawTier
}
records = append(records, subscriptionImportRecord{
ProviderName: "Telecom",
ProviderNameCn: "中国电信",
ProviderCountry: "CN",
ProviderWebsite: "https://www.ctyun.cn",
OperatorName: "CTYun",
OperatorNameCn: "天翼云",
OperatorCountry: "CN",
OperatorWebsite: "https://www.ctyun.cn",
OperatorType: "cloud",
PlanFamily: "token_plan",
PlanCode: "ctyun-token-plan-" + tierCode,
PlanName: planName,
Tier: rawTier,
BillingCycle: "monthly",
Currency: "CNY",
ListPrice: price,
PriceUnit: "CNY/pack",
QuotaValue: quotaValue,
QuotaUnit: "tokens/pack",
PlanScope: "Token Plan",
ModelScope: []string{strings.TrimSpace(match[3])},
SourceURL: defaultCTYunTokenPlanURL,
PublishedAt: publishedAt,
EffectiveDate: effectiveDateFromPublishedAt(publishedAt),
Notes: "天翼云大模型 AI 专项活动页套餐。",
})
}
return records, nil
}
func parseChineseTokenQuota(raw string) int64 {
cleaned := strings.TrimSpace(strings.TrimSuffix(raw, "Tokens包"))
cleaned = strings.ReplaceAll(cleaned, " ", "")
switch {
case strings.Contains(cleaned, "亿"):
return parseDecimalMultiplier(strings.TrimSuffix(cleaned, "亿"), 100000000)
case strings.Contains(cleaned, "万"):
return parseDecimalMultiplier(strings.TrimSuffix(cleaned, "万"), 10000)
default:
return mustParseSubscriptionInt64(cleaned)
}
}
func extractCTYunCodingModels(raw string) []string {
lines := strings.Split(raw, "\n")
models := make([]string, 0, 8)
capturing := false
for _, line := range lines {
line = strings.TrimSpace(line)
switch {
case line == "支持模型":
capturing = true
continue
case line == "用量限制":
return models
case !capturing || line == "":
continue
default:
models = append(models, line)
}
}
return models
}