Skip to content

Commit b2b6c7e

Browse files
夏一飞夏一飞
夏一飞
authored and
夏一飞
committed
feat: add model price synchronization feature
1 parent f7c5204 commit b2b6c7e

File tree

10 files changed

+463
-40
lines changed

10 files changed

+463
-40
lines changed

app/api/v1/models/route.ts

+42-16
Original file line numberDiff line numberDiff line change
@@ -68,24 +68,50 @@ export async function GET() {
6868

6969
// Get price information for all models
7070
const modelsWithPrices = await getOrCreateModelPrices(
71-
data.data.map((item) => ({
72-
id: String(item.id),
73-
name: String(item.name),
74-
base_model_id: item.info?.base_model_id,
75-
}))
71+
data.data.map((item) => {
72+
// 处理形如 gemini_search.gemini-2.0-flash 的派生模型ID
73+
let baseModelId = item.info?.base_model_id;
74+
75+
// 如果没有明确的base_model_id,尝试从ID中提取
76+
if (!baseModelId && item.id) {
77+
const idParts = String(item.id).split(".");
78+
if (idParts.length > 1) {
79+
baseModelId = idParts[idParts.length - 1];
80+
}
81+
}
82+
83+
return {
84+
id: String(item.id),
85+
name: String(item.name),
86+
base_model_id: baseModelId,
87+
};
88+
})
7689
);
7790

78-
const validModels = data.data.map((item, index) => ({
79-
id: modelsWithPrices[index].id,
80-
base_model_id: item.info?.base_model_id || "",
81-
name: modelsWithPrices[index].name,
82-
imageUrl: item.info?.meta?.profile_image_url || "/static/favicon.png",
83-
system_prompt: item.info?.params?.system || "",
84-
input_price: modelsWithPrices[index].input_price,
85-
output_price: modelsWithPrices[index].output_price,
86-
per_msg_price: modelsWithPrices[index].per_msg_price,
87-
updated_at: modelsWithPrices[index].updated_at,
88-
}));
91+
const validModels = data.data.map((item, index) => {
92+
// 处理形如 gemini_search.gemini-2.0-flash 的派生模型ID
93+
let baseModelId = item.info?.base_model_id || "";
94+
95+
// 如果没有明确的base_model_id,尝试从ID中提取
96+
if (!baseModelId && item.id) {
97+
const idParts = String(item.id).split(".");
98+
if (idParts.length > 1) {
99+
baseModelId = idParts[idParts.length - 1];
100+
}
101+
}
102+
103+
return {
104+
id: modelsWithPrices[index].id,
105+
base_model_id: baseModelId,
106+
name: modelsWithPrices[index].name,
107+
imageUrl: item.info?.meta?.profile_image_url || "/static/favicon.png",
108+
system_prompt: item.info?.params?.system || "",
109+
input_price: modelsWithPrices[index].input_price,
110+
output_price: modelsWithPrices[index].output_price,
111+
per_msg_price: modelsWithPrices[index].per_msg_price,
112+
updated_at: modelsWithPrices[index].updated_at,
113+
};
114+
});
89115

90116
return NextResponse.json(validModels);
91117
} catch (error) {
+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { NextRequest, NextResponse } from "next/server";
2+
import { pool } from "@/lib/db";
3+
4+
export async function POST(request: NextRequest) {
5+
try {
6+
const client = await pool.connect();
7+
try {
8+
// 1. 获取所有有效的派生模型(base_model_id 存在且在数据库中有对应记录)
9+
const derivedModelsResult = await client.query(`
10+
SELECT d.id, d.name, d.base_model_id
11+
FROM model_prices d
12+
JOIN model_prices b ON d.base_model_id = b.id
13+
WHERE d.base_model_id IS NOT NULL
14+
`);
15+
16+
if (derivedModelsResult.rows.length === 0) {
17+
return NextResponse.json({
18+
success: true,
19+
message: "No derived models found",
20+
syncedModels: [],
21+
});
22+
}
23+
24+
const derivedModels = derivedModelsResult.rows;
25+
const syncResults = [];
26+
27+
// 2. 为每个派生模型同步价格
28+
for (const derivedModel of derivedModels) {
29+
try {
30+
// 获取上游模型价格
31+
const baseModelResult = await client.query(
32+
`SELECT input_price, output_price, per_msg_price FROM model_prices WHERE id = $1`,
33+
[derivedModel.base_model_id]
34+
);
35+
36+
if (baseModelResult.rows.length === 0) {
37+
syncResults.push({
38+
id: derivedModel.id,
39+
name: derivedModel.name,
40+
success: false,
41+
error: "Base model not found",
42+
});
43+
continue;
44+
}
45+
46+
const baseModel = baseModelResult.rows[0];
47+
48+
// 更新派生模型价格
49+
const updateResult = await client.query(
50+
`UPDATE model_prices
51+
SET
52+
input_price = $2,
53+
output_price = $3,
54+
per_msg_price = $4,
55+
updated_at = CURRENT_TIMESTAMP
56+
WHERE id = $1
57+
RETURNING *`,
58+
[
59+
derivedModel.id,
60+
baseModel.input_price,
61+
baseModel.output_price,
62+
baseModel.per_msg_price,
63+
]
64+
);
65+
66+
const updatedModel = updateResult.rows[0];
67+
68+
syncResults.push({
69+
id: updatedModel.id,
70+
name: updatedModel.name,
71+
base_model_id: derivedModel.base_model_id,
72+
success: true,
73+
input_price: Number(updatedModel.input_price),
74+
output_price: Number(updatedModel.output_price),
75+
per_msg_price: Number(updatedModel.per_msg_price),
76+
});
77+
} catch (error) {
78+
console.error(`Error syncing model ${derivedModel.id}:`, error);
79+
syncResults.push({
80+
id: derivedModel.id,
81+
name: derivedModel.name,
82+
success: false,
83+
error: error instanceof Error ? error.message : "Unknown error",
84+
});
85+
}
86+
}
87+
88+
const successCount = syncResults.filter((r) => r.success).length;
89+
90+
return NextResponse.json({
91+
success: true,
92+
message: `Successfully synced ${successCount} of ${derivedModels.length} derived models`,
93+
syncedModels: syncResults,
94+
});
95+
} finally {
96+
client.release();
97+
}
98+
} catch (error) {
99+
console.error("Sync all prices failed:", error);
100+
return NextResponse.json(
101+
{
102+
error: "Sync all prices failed",
103+
message: error instanceof Error ? error.message : "Unknown error",
104+
},
105+
{ status: 500 }
106+
);
107+
}
108+
}
109+
110+
export async function OPTIONS() {
111+
return NextResponse.json({}, { status: 200 });
112+
}

app/api/v1/models/sync-price/route.ts

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { NextRequest, NextResponse } from "next/server";
2+
import { pool } from "@/lib/db";
3+
4+
export async function POST(request: NextRequest) {
5+
try {
6+
const data = await request.json();
7+
const { modelId } = data;
8+
9+
if (!modelId) {
10+
return NextResponse.json(
11+
{ error: "Model ID is required" },
12+
{ status: 400 }
13+
);
14+
}
15+
16+
const client = await pool.connect();
17+
try {
18+
// 1. 获取派生模型信息
19+
const derivedModelResult = await client.query(
20+
`SELECT id, name, base_model_id FROM model_prices WHERE id = $1`,
21+
[modelId]
22+
);
23+
24+
if (derivedModelResult.rows.length === 0) {
25+
return NextResponse.json({ error: "Model not found" }, { status: 404 });
26+
}
27+
28+
const derivedModel = derivedModelResult.rows[0];
29+
let baseModelId = derivedModel.base_model_id;
30+
31+
// 如果数据库中没有base_model_id,尝试从ID中提取
32+
if (!baseModelId) {
33+
const idParts = modelId.split(".");
34+
if (idParts.length > 1) {
35+
baseModelId = idParts[idParts.length - 1];
36+
37+
// 更新数据库中的base_model_id
38+
await client.query(
39+
`UPDATE model_prices SET base_model_id = $2 WHERE id = $1`,
40+
[modelId, baseModelId]
41+
);
42+
}
43+
}
44+
45+
if (!baseModelId) {
46+
return NextResponse.json(
47+
{ error: "Model does not have a base model" },
48+
{ status: 400 }
49+
);
50+
}
51+
52+
// 2. 获取上游模型价格
53+
const baseModelResult = await client.query(
54+
`SELECT input_price, output_price, per_msg_price FROM model_prices WHERE id = $1`,
55+
[baseModelId]
56+
);
57+
58+
if (baseModelResult.rows.length === 0) {
59+
return NextResponse.json(
60+
{ error: "Base model not found" },
61+
{ status: 404 }
62+
);
63+
}
64+
65+
const baseModel = baseModelResult.rows[0];
66+
67+
// 3. 更新派生模型价格
68+
const updateResult = await client.query(
69+
`UPDATE model_prices
70+
SET
71+
input_price = $2,
72+
output_price = $3,
73+
per_msg_price = $4,
74+
updated_at = CURRENT_TIMESTAMP
75+
WHERE id = $1
76+
RETURNING *`,
77+
[
78+
modelId,
79+
baseModel.input_price,
80+
baseModel.output_price,
81+
baseModel.per_msg_price,
82+
]
83+
);
84+
85+
const updatedModel = updateResult.rows[0];
86+
87+
return NextResponse.json({
88+
success: true,
89+
message: `Successfully synced prices from ${baseModelId} to ${modelId}`,
90+
data: {
91+
id: updatedModel.id,
92+
name: updatedModel.name,
93+
base_model_id: baseModelId,
94+
input_price: Number(updatedModel.input_price),
95+
output_price: Number(updatedModel.output_price),
96+
per_msg_price: Number(updatedModel.per_msg_price),
97+
updated_at: updatedModel.updated_at,
98+
},
99+
});
100+
} finally {
101+
client.release();
102+
}
103+
} catch (error) {
104+
console.error("Sync price failed:", error);
105+
return NextResponse.json(
106+
{
107+
error: "Sync price failed",
108+
message: error instanceof Error ? error.message : "Unknown error",
109+
},
110+
{ status: 500 }
111+
);
112+
}
113+
}
114+
115+
export async function OPTIONS() {
116+
return NextResponse.json({}, { status: 200 });
117+
}

0 commit comments

Comments
 (0)