mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-07-30 23:36:15 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3396e0cd47 | ||
|
|
855d60b4a5 | ||
|
|
27ace348b5 | ||
|
|
73e3d34623 |
@@ -2516,7 +2516,7 @@ async def get_provider_model(provider_id: int, model_id: str) -> dict[str, objec
|
||||
status_code=404, detail="Model not found for this provider"
|
||||
)
|
||||
return _row_to_model(
|
||||
row, apply_provider_fee=True, provider_fee=provider.provider_fee
|
||||
row, apply_provider_fee=False, provider_fee=provider.provider_fee
|
||||
).dict() # type: ignore
|
||||
|
||||
|
||||
@@ -2729,7 +2729,10 @@ async def get_provider_models(provider_id: int) -> dict[str, object]:
|
||||
raise HTTPException(status_code=404, detail="Provider not found")
|
||||
|
||||
db_models = await list_models(
|
||||
session=session, upstream_id=provider_id, include_disabled=True
|
||||
session=session,
|
||||
upstream_id=provider_id,
|
||||
include_disabled=True,
|
||||
apply_fees=False,
|
||||
)
|
||||
|
||||
upstream_models = []
|
||||
@@ -2737,10 +2740,7 @@ async def get_provider_models(provider_id: int) -> dict[str, object]:
|
||||
if upstream_instance:
|
||||
try:
|
||||
raw_models = await upstream_instance.fetch_models()
|
||||
upstream_models = [
|
||||
upstream_instance._apply_provider_fee_to_model(m)
|
||||
for m in raw_models
|
||||
]
|
||||
upstream_models = raw_models
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Failed to fetch models from {provider.provider_type}: {e}"
|
||||
|
||||
@@ -291,9 +291,7 @@ async def raw_send_to_lnurl(
|
||||
lnurl_data["callback_url"], final_amount
|
||||
)
|
||||
|
||||
melt_quote_resp = await wallet.melt_quote(
|
||||
invoice=bolt11_invoice, amount_msat=final_amount
|
||||
)
|
||||
melt_quote_resp = await wallet.melt_quote(invoice=bolt11_invoice)
|
||||
|
||||
if amount:
|
||||
proofs, _ = await wallet.select_to_send(proofs, amount, set_reserved=True)
|
||||
|
||||
@@ -230,6 +230,7 @@ async def list_models(
|
||||
session: AsyncSession,
|
||||
upstream_id: int,
|
||||
include_disabled: bool = False,
|
||||
apply_fees: bool = True,
|
||||
) -> list[Model]:
|
||||
from sqlmodel import select
|
||||
|
||||
@@ -247,7 +248,7 @@ async def list_models(
|
||||
return [
|
||||
_row_to_model(
|
||||
r,
|
||||
apply_provider_fee=True,
|
||||
apply_provider_fee=apply_fees,
|
||||
provider_fee=providers_by_id[r.upstream_provider_id].provider_fee
|
||||
if r.upstream_provider_id in providers_by_id
|
||||
else 1.01,
|
||||
|
||||
@@ -808,45 +808,6 @@ export function AddProviderModelDialog({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='max_prompt_cost'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Max Prompt Cost</FormLabel>
|
||||
<FormControl>
|
||||
<Input type='number' step='0.0001' {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='max_completion_cost'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Max Completion Cost</FormLabel>
|
||||
<FormControl>
|
||||
<Input type='number' step='0.0001' {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='max_cost'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Max Total Cost</FormLabel>
|
||||
<FormControl>
|
||||
<Input type='number' step='0.0001' {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -139,18 +139,26 @@ export class AdminService {
|
||||
): Record<string, unknown> {
|
||||
if (!pricing) return pricing;
|
||||
const result = { ...pricing };
|
||||
if (typeof result.prompt === 'number') {
|
||||
result.prompt = result.prompt * 1000000;
|
||||
}
|
||||
if (typeof result.completion === 'number') {
|
||||
result.completion = result.completion * 1000000;
|
||||
}
|
||||
if (typeof result.request === 'number') {
|
||||
result.request = result.request * 1000000;
|
||||
}
|
||||
if (typeof result.image === 'number') {
|
||||
result.image = result.image * 1000000;
|
||||
}
|
||||
|
||||
// Only prompt and completion are per-token and need scaling to per-1M
|
||||
const convertField = (field: string) => {
|
||||
const val = result[field];
|
||||
if (val !== undefined && val !== null) {
|
||||
const num = typeof val === 'string' ? parseFloat(val) : (val as number);
|
||||
if (!isNaN(num)) {
|
||||
// Multiply by 1M and round to avoid floating point artifacts (e.g. 0.40399999999999997)
|
||||
// 9 decimals is plenty for USD/1M tokens (0.000000001)
|
||||
result[field] = parseFloat((num * 1000000).toFixed(9));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
convertField('prompt');
|
||||
convertField('completion');
|
||||
|
||||
// Other fields (request, image, etc.) are already flat fees (per item)
|
||||
// so we do NOT scale them.
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -159,18 +167,23 @@ export class AdminService {
|
||||
): Record<string, unknown> {
|
||||
if (!pricing) return pricing;
|
||||
const result = { ...pricing };
|
||||
if (typeof result.prompt === 'number') {
|
||||
result.prompt = result.prompt / 1000000;
|
||||
}
|
||||
if (typeof result.completion === 'number') {
|
||||
result.completion = result.completion / 1000000;
|
||||
}
|
||||
if (typeof result.request === 'number') {
|
||||
result.request = result.request / 1000000;
|
||||
}
|
||||
if (typeof result.image === 'number') {
|
||||
result.image = result.image / 1000000;
|
||||
}
|
||||
|
||||
// Only prompt and completion are per-1M in UI and need scaling down to per-token
|
||||
const convertField = (field: string) => {
|
||||
const val = result[field];
|
||||
if (val !== undefined && val !== null) {
|
||||
const num = typeof val === 'string' ? parseFloat(val) : (val as number);
|
||||
if (!isNaN(num)) {
|
||||
result[field] = num / 1000000;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
convertField('prompt');
|
||||
convertField('completion');
|
||||
|
||||
// Other fields stay as flat fees
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -291,7 +304,19 @@ export class AdminService {
|
||||
const data = await apiClient.get<ProviderModels>(
|
||||
`/admin/api/upstream-providers/${providerId}/models`
|
||||
);
|
||||
return data;
|
||||
|
||||
// Convert pricing for all models in the list so the UI receives "per 1M tokens" values
|
||||
return {
|
||||
...data,
|
||||
db_models: data.db_models.map((m) => ({
|
||||
...m,
|
||||
pricing: this.convertPricingToPerMillionTokens(m.pricing),
|
||||
})),
|
||||
remote_models: data.remote_models.map((m) => ({
|
||||
...m,
|
||||
pricing: this.convertPricingToPerMillionTokens(m.pricing),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
static async createProviderModel(
|
||||
@@ -498,8 +523,8 @@ export class AdminService {
|
||||
: null;
|
||||
|
||||
const pricing = {
|
||||
prompt: (data.input_cost as number) / 1000000,
|
||||
completion: (data.output_cost as number) / 1000000,
|
||||
prompt: data.input_cost as number,
|
||||
completion: data.output_cost as number,
|
||||
request: (data.min_cost_per_request as number) || 0,
|
||||
image: 0,
|
||||
web_search: 0,
|
||||
@@ -553,8 +578,8 @@ export class AdminService {
|
||||
const existingModel = await this.getModel(modelId, providerId);
|
||||
|
||||
const pricing = {
|
||||
prompt: (data.input_cost as number) / 1000000,
|
||||
completion: (data.output_cost as number) / 1000000,
|
||||
prompt: data.input_cost as number,
|
||||
completion: data.output_cost as number,
|
||||
request: (data.min_cost_per_request as number) || 0,
|
||||
image: 0,
|
||||
web_search: 0,
|
||||
|
||||
Reference in New Issue
Block a user