Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
152 commits
Select commit Hold shift + click to select a range
d693b55
Add configurable excluded model IDs setting
sh1ftred Oct 17, 2025
fa0b283
Remove models variable from base URL output
sh1ftred Oct 17, 2025
80a7f5d
Replace hardcoded model exclusions with configurable setting
sh1ftred Oct 17, 2025
859b31e
Update tests to reflect removal of models field from base URL output
sh1ftred Oct 17, 2025
fe2b184
Add EXCLUDED_MODEL_IDS configuration to .env.example
sh1ftred Oct 17, 2025
fe66f24
small fixes
GitHappens2Me Nov 11, 2025
a5ac510
Merge remote-tracking branch 'upstream/main' into minor-fixes
GitHappens2Me Nov 11, 2025
34cdbfe
add logs page
Nov 15, 2025
3e7d4c6
add date picker
Nov 15, 2025
23ff99d
copy button
Nov 15, 2025
26110a6
add logs page
Nov 15, 2025
99d98ff
feat: Add usage tracking dashboard and API
cursoragent Nov 15, 2025
38356d7
add copy button & order entries
Nov 16, 2025
dbffef6
fixed streaming responses for unsupported content-encoding
GitHappens2Me Nov 17, 2025
0bcf7bb
rm accidental push of cdk-python binaries
shroominic Nov 17, 2025
c5c032b
fixed false positive redactions and added unit tests
GitHappens2Me Nov 18, 2025
f45ff16
api cheat sheet
shroominic Nov 20, 2025
1a2b52a
fix ui build
shroominic Nov 21, 2025
028e739
make login page dark mode
shroominic Nov 21, 2025
177ea25
Merge main into feature/configurable-model-exclusions
cursoragent Nov 22, 2025
071444f
Merge pull request #200 from Routstr/fix/remove-models-from-base-url-…
shroominic Nov 22, 2025
83a49a3
Merge pull request #202 from Routstr/feature/configurable-model-exclu…
shroominic Nov 22, 2025
e71baeb
Merge pull request #232 from Routstr/rm-cdk-python-binaries
shroominic Nov 22, 2025
19575eb
Merge branch 'v0.2.1' into minor-fixes
shroominic Nov 22, 2025
8a70e19
Merge pull request #231 from GitHappens2Me/fix/stream-encoding
shroominic Nov 22, 2025
6d6f66d
Merge pull request #236 from Routstr/node-landingpage-clean
shroominic Nov 22, 2025
dacabaa
fix pytests
shroominic Nov 22, 2025
edd7bd4
Merge pull request #224 from GitHappens2Me/minor-fixes
shroominic Nov 22, 2025
0f23335
first shot
Nov 22, 2025
12515cb
clean up
Nov 22, 2025
ec60dbe
half undo 83a49a3 to not introduce extra db-settings
shroominic Nov 23, 2025
13640d1
Merge remote-tracking branch 'remotes/origin/cursor/add-usage-trackin…
cursoragent Nov 23, 2025
2438f32
fix docker build
shroominic Nov 22, 2025
eb83b3c
refactor: unify logs and usage implementation
cursoragent Nov 23, 2025
86b1ba0
fix typing+linting
shroominic Nov 22, 2025
e154f65
fix revenue tracking problems
shroominic Nov 23, 2025
f136383
fix provider getattr
shroominic Nov 23, 2025
d0aa91a
feat(ui): enhance dashboard visualizations
shroominic Nov 23, 2025
85962aa
refactor(ui): restructure dashboard layout
shroominic Nov 23, 2025
dcfd398
feat(ui): add balance summary to main dashboard
shroominic Nov 23, 2025
64b45a7
feat(ui): implement global currency selection
shroominic Nov 23, 2025
3371b03
feat(ui): update currency toggle to minimalistic dropdown
shroominic Nov 23, 2025
6f4e57e
feat(ui): apply global currency formatting to stats components
shroominic Nov 23, 2025
c5b448b
fix search
shroominic Nov 23, 2025
d142ca5
fix date picker
shroominic Nov 23, 2025
2cbf7b4
fix build error
shroominic Nov 23, 2025
f7d6a0e
Merge branch 'origin/v0.2.1' into cursor/recreate-branch-with-origina…
cursoragent Nov 23, 2025
30fc8d9
Merge pull request #241 from Routstr/cursor/recreate-branch-with-orig…
shroominic Nov 23, 2025
f5be987
v0.2.1 bump
shroominic Nov 23, 2025
0d698e9
Merge pull request #242 from Routstr/login-dark-mode
shroominic Nov 23, 2025
684ab63
fix auto redirect to /login
shroominic Nov 23, 2025
ed15f61
removed the unused /_register route
shroominic Nov 23, 2025
d6fdf0c
display all providers
9qeklajc Nov 24, 2025
a67cd2d
Merge pull request #243 from Routstr/fix-display-when-no-model-exists
9qeklajc Nov 24, 2025
afa4a59
improve performance
9qeklajc Nov 24, 2025
95ffc61
openai lib
9qeklajc Nov 24, 2025
5eb4a40
fix
9qeklajc Nov 24, 2025
8208a87
fix linting errors
shroominic Nov 28, 2025
30fd369
Merge branch 'v0.2.1' into fix/logging_redaction
shroominic Nov 28, 2025
4ed4f61
fix linting
shroominic Nov 28, 2025
6f00478
fix typing issues
shroominic Nov 28, 2025
e59335d
Merge pull request #233 from GitHappens2Me/fix/logging_redaction
shroominic Nov 28, 2025
7b6e00e
ruff format
shroominic Nov 28, 2025
432fe48
Merge branch 'v0.2.1' into gemini-upstream
shroominic Nov 28, 2025
b76598e
fix reserved balance race condition bug
shroominic Dec 1, 2025
ee185f5
editable provider fee
shroominic Dec 2, 2025
f116a5e
update provider fee label
shroominic Dec 2, 2025
83818e1
Merge pull request #246 from Routstr/fix-reserved–balance-race-condition
shroominic Dec 2, 2025
65e7702
merge ppq integration
shroominic Dec 2, 2025
42c2ddb
optimize async startup process
shroominic Dec 2, 2025
a226a78
rm unused functions
shroominic Dec 2, 2025
80359d0
add required openai dep
shroominic Dec 2, 2025
2b4f71c
Merge branch 'v0.2.1' into gemini-upstream
shroominic Dec 2, 2025
6b4b392
feat(ui): make admin settings dynamic based on backend response
shroominic Dec 2, 2025
d0790dc
use openrouter model matching
9qeklajc Dec 3, 2025
d7611e7
remove model prefix for gemini
9qeklajc Dec 3, 2025
e7f677b
clean up
9qeklajc Dec 3, 2025
355f860
embedding integration
9qeklajc Dec 3, 2025
7bc9ee0
Merge branch 'gemini-upstream' into embeddings
9qeklajc Dec 3, 2025
8df0c17
embedding integration
9qeklajc Dec 3, 2025
195da0c
embedding integration
9qeklajc Dec 3, 2025
5d22198
embedding integration
9qeklajc Dec 3, 2025
9438bc9
clean up
9qeklajc Dec 3, 2025
d6648d3
Merge pull request #239 from Routstr/gemini-upstream
9qeklajc Dec 3, 2025
bc8c08c
open router free model breaks openrouter model fetching
9qeklajc Dec 5, 2025
fd5ec01
fix ppq models matching incorrectly
shroominic Dec 9, 2025
4ac257d
fix model fetching issue
shroominic Dec 9, 2025
a363c7a
feat: openrouter balance fetch + generic get_balance
shroominic Dec 9, 2025
b88ef85
ignore ppq auto model
shroominic Dec 9, 2025
34d1e4b
populate upstream_provider_id
shroominic Dec 9, 2025
5a67e6b
ignore openrouter/bodybuilder
shroominic Dec 9, 2025
20bf5e3
feat: add custom models and overrides
shroominic Dec 9, 2025
02ca361
fix linting typing/errors
shroominic Dec 9, 2025
a150d38
Merge branch 'v0.2.1' into model-fetch-issue
shroominic Dec 10, 2025
21340b2
check for valid_pricing
shroominic Dec 10, 2025
085ff75
create and topup token with lightning
9qeklajc Dec 10, 2025
7ca6906
make format required & fix ui formating
9qeklajc Dec 10, 2025
2f841df
Merge pull request #254 from Routstr/format
shroominic Dec 11, 2025
4d67af5
rm shaky test
shroominic Dec 11, 2025
2aee75e
Merge pull request #255 from Routstr/rm-shaky
shroominic Dec 11, 2025
5738b1b
rm hardcoded excluded models
shroominic Dec 11, 2025
b8c34af
improve logging
shroominic Dec 11, 2025
7f918ea
Merge pull request #252 from Routstr/model-fetch-issue
shroominic Dec 11, 2025
e4eda59
Merge pull request #256 from Routstr/improve-console-logging
shroominic Dec 11, 2025
2cc5063
Merge branch 'origin/v0.2.1' into lightning to resolve conflicts
cursoragent Dec 11, 2025
4c7887f
update algorithm logs
shroominic Dec 11, 2025
1e37c42
fix ui build
shroominic Dec 11, 2025
c97c74a
cleanup logs
shroominic Dec 11, 2025
29129f8
prettier
shroominic Dec 11, 2025
87b1443
Merge pull request #253 from Routstr/lightning
shroominic Dec 11, 2025
329d223
Merge branch 'v0.2.1' into embeddings-integration
shroominic Dec 11, 2025
c0176a5
Merge branch 'v0.2.1' into embeddings-integration
shroominic Dec 11, 2025
72b281b
Merge branch 'v0.2.1' into refactor-remove-unused-functions
shroominic Dec 11, 2025
52601f8
ruff fix
shroominic Dec 11, 2025
19b5f28
Merge pull request #249 from Routstr/refactor-remove-unused-functions
shroominic Dec 11, 2025
5473658
add simple test
shroominic Dec 11, 2025
c11cc10
Merge pull request #248 from Routstr/feature/dynamic-settings
shroominic Dec 11, 2025
5db9abc
remove flaky wallet tests
shroominic Dec 11, 2025
590fb4b
ruff fix
shroominic Dec 11, 2025
06770a0
Merge branch 'v0.2.1' into embeddings-integration
9qeklajc Dec 12, 2025
43e9732
fix model naming issue in response
9qeklajc Dec 12, 2025
82d2627
added reponse api
9qeklajc Dec 15, 2025
8edc351
Merge pull request #258 from Routstr/remove-flaky-wallet-tests
shroominic Dec 19, 2025
5416cef
use cost field if available
9qeklajc Dec 20, 2025
301dd81
include openrouters upstream_inference_cost + manually add gemini ima…
shroominic Dec 21, 2025
e39742c
rm completion_image pricing with manual overrides
shroominic Dec 22, 2025
d203370
Merge pull request #265 from Routstr/update-cost-calculation
shroominic Dec 22, 2025
cc42534
manual alias due to openrouter api bug
shroominic Dec 22, 2025
dd4ed75
undo 43e9732
shroominic Dec 22, 2025
b01c7b2
Merge branch 'v0.2.1' into embeddings
9qeklajc Dec 22, 2025
9594e9f
Merge pull request #251 from Routstr/embeddings-integration
shroominic Dec 23, 2025
a6d0bd1
update aliases
9qeklajc Dec 23, 2025
5b8e56f
Merge branch 'v0.2.1' into embedding-with-aliases
9qeklajc Dec 23, 2025
0fa3e77
more examples for devs and testing
shroominic Dec 24, 2025
2c404c6
ruff fix
shroominic Dec 24, 2025
41fd2e2
fix typing
shroominic Dec 24, 2025
a3e8d5f
ignore cloudflare headers from logs
shroominic Dec 24, 2025
71e7c21
add missing migration
shroominic Dec 25, 2025
b9418db
rm model cleanup task
shroominic Dec 25, 2025
0c61fde
ruff format
shroominic Dec 25, 2025
ee508cb
rm model cleanup
shroominic Dec 25, 2025
b54812c
fix: alias_id not displayed correctly by dashboard
shroominic Dec 25, 2025
525476c
fix alias not displaying correctly
shroominic Dec 25, 2025
84b0007
force sats pricig
9qeklajc Dec 26, 2025
1c6a603
Merge pull request #271 from Routstr/embedding-with-aliases
9qeklajc Dec 26, 2025
c064452
Merge branch 'v0.2.1' into force-sats-pricing
9qeklajc Dec 26, 2025
2d247dd
fmt
9qeklajc Dec 26, 2025
ea655b7
optimize startup time
shroominic Dec 26, 2025
634a473
Merge pull request #276 from Routstr/force-sats-pricing
shroominic Dec 26, 2025
4418d87
Merge branch 'v0.2.1' into openai-responses-api
9qeklajc Dec 26, 2025
f4b014c
Merge remote-tracking branch 'origin/examples' into openai-responses-api
shroominic Dec 27, 2025
eed5bc5
Merge pull request #278 from Routstr/openai-responses-api
shroominic Dec 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ compose.testing.yml
.todo
.github
.vscode
.DS_Store
.DS_Store
**/node_modules
ui/.next
8 changes: 6 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,18 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
node-version: "18"
cache: "npm"
cache-dependency-path: ui/package-lock.json

- name: Install UI dependencies
working-directory: ./ui
run: npm ci

- name: Run UI format check
working-directory: ./ui
run: npm run format-check

- name: Run UI linting
working-directory: ./ui
run: npm run lint
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ Once built, the UI is automatically served by the FastAPI backend:

- **Dashboard**: `http://localhost:8000/`
- **Login**: `http://localhost:8000/login`
- **Models Management**: `http://localhost:8000/model
- **Models Management**: `http://localhost:8000/model`
- **Providers Management**: `http://localhost:8000/providers`
- **Settings**: `http://localhost:8000/settings`

Expand Down
37 changes: 0 additions & 37 deletions example.py

This file was deleted.

11 changes: 11 additions & 0 deletions examples/balance/check_balance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import os

import httpx

# Use your Cashu token or API key as the Bearer token,
# cashu token is hashed on the server and acts as an Temporary API key
headers = {"Authorization": f"Bearer {os.environ.get('TOKEN')}"}
base_url = os.environ.get("API_URL", "https://api.routstr.com/v1")

resp = httpx.get(f"{base_url}/balance/info", headers=headers)
print(resp.json())
15 changes: 15 additions & 0 deletions examples/balance/create_balance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import os

import httpx

# Send a Cashu token to the /create endpoint to get a persistent API key
token = os.environ.get("TOKEN")
if not token:
print("Please set TOKEN environment variable with a Cashu token")
exit(1)

base_url = os.environ.get("API_URL", "https://api.routstr.com/v1")

resp = httpx.get(f"{base_url}/balance/create", params={"initial_balance_token": token})

print(resp.json())
12 changes: 12 additions & 0 deletions examples/balance/refund_balance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import os

import httpx

# Use your Cashu token or API key as the Bearer token
headers = {"Authorization": f"Bearer {os.environ.get('TOKEN')}"}
base_url = os.environ.get("API_URL", "https://api.routstr.com/v1")

resp = httpx.post(f"{base_url}/balance/refund", headers=headers)

print("Refund successful!")
print(resp.json())
16 changes: 16 additions & 0 deletions examples/balance/topup_balance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import os

import httpx

# Use your Cashu token or API key as the Bearer token
headers = {"Authorization": f"Bearer {os.environ.get('TOKEN')}"}
base_url = os.environ.get("API_URL", "https://api.routstr.com/v1")

# The Cashu token to top up with
cashu_token = input("Enter Cashu token to top up: ")

resp = httpx.post(
f"{base_url}/balance/topup", headers=headers, json={"cashu_token": cashu_token}
)

print(resp.json())
15 changes: 15 additions & 0 deletions examples/chat_completions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import os

from openai import OpenAI

client = OpenAI(
api_key=os.environ.get("TOKEN"),
base_url=os.environ.get("API_URL", "https://api.routstr.com/v1"),
)

response = client.chat.completions.create(
model=os.environ.get("MODEL", "gpt-5-nano"),
messages=[{"role": "user", "content": "Hello!"}],
)

print(response.choices[0].message.content)
19 changes: 19 additions & 0 deletions examples/list_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import os

import httpx
from openai import OpenAI

client = OpenAI(
api_key=os.environ.get("TOKEN", ""),
base_url=os.environ.get("API_URL", "https://api.routstr.com/v1"),
)

for model in client.models.list():
print(model.id)

# OR

models = httpx.get(
f"{client.base_url}/v1/models",
headers={"Authorization": f"Bearer {client.api_key}"},
).json()
31 changes: 31 additions & 0 deletions examples/responses/conversation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import os

from openai import OpenAI

client = OpenAI(
api_key=os.environ.get("TOKEN"),
base_url=os.environ.get("API_URL", "https://api.routstr.com/v1"),
)

conversation = [] # type: ignore

# First turn
response1 = client.responses.create( # type: ignore
model="o4-mini",
input="Hi, my name is Alice.",
conversation=conversation,
)
print("Response 1:", response1.output)

# Note: The 'conversation' parameter might need to be constructed differently
# depending on exact SDK/API spec. Typically, you pass back the previous turn's data.
# Assuming the SDK manages or returns a conversation object/ID:
# conversation.append(response1)

# Second turn - demonstrating intent, actual implementation depends on strict API spec
# response2 = client.responses.create(
# model="openai/gpt-4o-mini",
# input="What is my name?",
# conversation=conversation,
# )
# print("Response 2:", response2.output)
17 changes: 17 additions & 0 deletions examples/responses/create.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import os

from openai import OpenAI

# The OpenAI SDK handles the 'responses' endpoint if it's updated to the latest version
# and the base_url points to a compatible proxy like Routstr.
client = OpenAI(
api_key=os.environ.get("TOKEN"),
base_url=os.environ.get("API_URL", "https://api.routstr.com/v1"),
)

response = client.responses.create(
model="gpt-5-mini",
input="Tell me a three sentence bedtime story about a unicorn.",
)

print(response.output)
20 changes: 20 additions & 0 deletions examples/responses/streaming_response.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import os

from openai import OpenAI

client = OpenAI(
api_key=os.environ.get("TOKEN"),
base_url=os.environ.get("API_URL", "https://api.routstr.com/v1"),
)

stream = client.responses.create(
model="claude-4.5-sonnet",
input="Write a short poem about rust.",
stream=True,
)

for event in stream:
# Note: Depending on the SDK version and response structure,
# you might access event.output_delta or similar fields
print(event, end="", flush=True)
print()
16 changes: 16 additions & 0 deletions examples/responses/web_search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import os

from openai import OpenAI

client = OpenAI(
api_key=os.environ.get("TOKEN"),
base_url=os.environ.get("API_URL", "https://api.routstr.com/v1"),
)

response = client.responses.create(
model="gpt-5-mini",
input="What is the latest news about AI?",
tools=[{"type": "web_search"}], # type: ignore
)

print(response.output)
28 changes: 28 additions & 0 deletions examples/streaming.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import os

from openai import OpenAI

client = OpenAI(
api_key=os.environ.get("TOKEN"),
base_url=os.environ.get("API_URL", "https://api.routstr.com/v1"),
)

messages = []
while True:
messages.append({"role": "user", "content": input("\nYou: ")})

stream = client.chat.completions.create(
model=os.environ.get("MODEL", "gpt-5.1-mini"),
messages=messages, # type: ignore
stream=True,
)

print("AI: ", end="")
response_content = ""
for chunk in stream:
if content := chunk.choices[0].delta.content: # type: ignore
print(content, end="", flush=True)
response_content += content
print()

messages.append({"role": "assistant", "content": response_content})
20 changes: 20 additions & 0 deletions examples/tor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import os

import httpx
from openai import OpenAI

# Requires `pip install "httpx[socks]"` and a running Tor proxy on port 9050
client = OpenAI(
api_key=os.environ.get("TOKEN"),
base_url=os.environ.get("ONION_URL", "http://roustrjfsdgfiueghsklchg.onion/v1"),
http_client=httpx.Client(proxies="socks5://localhost:9050"),
)

print(
client.chat.completions.create(
model="openai/gpt-4o-mini",
messages=[{"role": "user", "content": "Hello from Tor!"}],
)
.choices[0]
.message.content
)
37 changes: 37 additions & 0 deletions migrations/versions/b9667ffc5701_alias_ids.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""alias-ids

Revision ID: b9667ffc5701
Revises: lightning_invoices
Create Date: 2025-12-25 19:30:44.673350
"""

import sqlalchemy as sa
import sqlmodel
from alembic import op

# revision identifiers, used by Alembic.
revision = "b9667ffc5701"
down_revision = "lightning_invoices"
branch_labels = None
depends_on = None


def upgrade() -> None:
# ### commands auto generated by Alembic ###
op.add_column(
"models",
sa.Column("canonical_slug", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
)
op.add_column(
"models",
sa.Column("alias_ids", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
)

# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("models", "alias_ids")
op.drop_column("models", "canonical_slug")
# ### end Alembic commands ###
39 changes: 39 additions & 0 deletions migrations/versions/lightning_invoices_table.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""Add lightning_invoices table

Revision ID: lightning_invoices
Revises: a1a1a1a1a1a1
Create Date: 2025-12-10 21:00:00.000000
"""

import sqlalchemy as sa
import sqlmodel
from alembic import op

revision = "lightning_invoices"
down_revision = "a1a1a1a1a1a1"
branch_labels = None
depends_on = None


def upgrade() -> None:
op.create_table(
"lightning_invoices",
sa.Column("id", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column("bolt11", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column("amount_sats", sa.Integer(), nullable=False),
sa.Column("description", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column("payment_hash", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column("status", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column("api_key_hash", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
sa.Column("purpose", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
sa.Column("created_at", sa.Integer(), nullable=False),
sa.Column("expires_at", sa.Integer(), nullable=False),
sa.Column("paid_at", sa.Integer(), nullable=True),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("bolt11"),
sa.UniqueConstraint("payment_hash"),
)


def downgrade() -> None:
op.drop_table("lightning_invoices")
Loading
Loading