Skip to content

Commit ab2df1e

Browse files
authored
Merge pull request #112 from sauagarwa/fix/unauthenticated-user
fix: order user id to use the same user for unauthenticated user and fix LangChain tool invocation
2 parents 658f0cb + 61322aa commit ab2df1e

File tree

7 files changed

+51
-72
lines changed

7 files changed

+51
-72
lines changed

packages/api/src/auth/middleware.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,10 @@ async def get_test_user(email: str, session: AsyncSession) -> dict:
289289

290290
async def get_dev_fallback_user(session: AsyncSession) -> dict:
291291
"""Get fallback dev user (first user or mock) - current behavior"""
292-
# Try to get first user from database
292+
# Try to get first user from database (ordered by ID for consistency)
293293
if session and User:
294294
try:
295-
result = await session.execute(select(User).limit(1))
295+
result = await session.execute(select(User).order_by(User.id).limit(1))
296296
db_user = result.scalar_one_or_none()
297297

298298
if db_user:

packages/api/src/services/alerts/generate_alert_graph.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,10 @@ def generate_alert(state):
4343
RunnableLambda(
4444
lambda state: {
4545
**state,
46-
'sql_query': parse_alert_to_sql_with_context(
47-
{
48-
'transaction': state['transaction'],
49-
'alert_text': state['alert_text'],
50-
'alert_rule': state['alert_rule'],
51-
}
46+
'sql_query': parse_alert_to_sql_with_context.func(
47+
state['transaction'],
48+
state['alert_text'],
49+
state['alert_rule'],
5250
),
5351
}
5452
),
@@ -58,7 +56,7 @@ def generate_alert(state):
5856
graph.add_node(
5957
'execute_sql',
6058
RunnableLambda(
61-
lambda state: {**state, 'query_result': execute_sql(state['sql_query'])}
59+
lambda state: {**state, 'query_result': execute_sql.func(state['sql_query'])}
6260
),
6361
)
6462

@@ -82,14 +80,12 @@ def generate_alert(state):
8280
RunnableLambda(
8381
lambda state: {
8482
**state,
85-
'alert_message': generate_alert_message(
86-
{
87-
'transaction': state['transaction'],
88-
'query_result': state['query_result'],
89-
'alert_text': state['alert_text'],
90-
'alert_rule': state['alert_rule'],
91-
'user': state['user'],
92-
}
83+
'alert_message': generate_alert_message.func(
84+
state['transaction'],
85+
state['query_result'],
86+
state['alert_text'],
87+
state['alert_rule'],
88+
state['user'],
9389
)
9490
if state.get('alert_triggered')
9591
else '',

packages/api/src/services/alerts/parse_alert_graph.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# app.py
21
from langchain_core.runnables import RunnableLambda
32
from langgraph.graph import StateGraph
43

@@ -25,12 +24,10 @@ class AppState(dict):
2524
RunnableLambda(
2625
lambda state: {
2726
**state,
28-
'sql_query': parse_alert_to_sql_with_context(
29-
{
30-
'transaction': state['transaction'],
31-
'alert_text': state['alert_text'],
32-
'alert_rule': state['alert_rule'],
33-
}
27+
'sql_query': parse_alert_to_sql_with_context.func(
28+
state['transaction'],
29+
state['alert_text'],
30+
state['alert_rule'],
3431
),
3532
}
3633
),
@@ -52,7 +49,7 @@ class AppState(dict):
5249
graph.add_node(
5350
'execute_sql',
5451
RunnableLambda(
55-
lambda state: {**state, 'query_result': execute_sql(state['sql_query'])}
52+
lambda state: {**state, 'query_result': execute_sql.func(state['sql_query'])}
5653
),
5754
)
5855

packages/api/src/services/alerts/validate_rule_graph.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,17 @@ def parse_alert_node(state):
4141
"""Parse alert text to SQL query"""
4242
return {
4343
**state,
44-
'sql_query': parse_alert_to_sql_with_context.invoke(
45-
{
46-
'transaction': state['transaction'],
47-
'alert_text': state['alert_text'],
48-
'alert_rule': state['alert_rule'],
49-
}
44+
'sql_query': parse_alert_to_sql_with_context.func(
45+
state['transaction'],
46+
state['alert_text'],
47+
state['alert_rule'],
5048
),
5149
}
5250

5351

5452
def execute_sql_node(state):
5553
"""Execute SQL query to validate it works"""
56-
return {**state, 'query_result': execute_sql.invoke(state['sql_query'])}
54+
return {**state, 'query_result': execute_sql.func(state['sql_query'])}
5755

5856

5957
def validate_sql_node(state):

packages/ui/src/routes/_protected/index.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ function Index() {
3838
};
3939

4040
const calculateChange = (current: number, previous: number) => {
41+
// Handle division by zero
42+
if (previous === 0) {
43+
if (current === 0) {
44+
return { value: '0.0', isPositive: true };
45+
}
46+
// If previous was 0 and current is not, show as 100% increase
47+
return { value: '100.0', isPositive: true };
48+
}
49+
4150
const change = ((current - previous) / previous) * 100;
4251
return {
4352
value: Math.abs(change).toFixed(1),

packages/ui/src/services/transaction.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,19 @@ export class TransactionService {
235235
userId: string,
236236
): Promise<Transaction> {
237237
// Transform form data to backend API format
238+
// Create transaction_date: use selected date but with current time (not midnight)
239+
// Parse date in local timezone to avoid timezone conversion issues
240+
const [year, month, day] = formData.date.split('-').map(Number);
241+
const now = new Date();
242+
const selectedDate = new Date(
243+
year,
244+
month - 1, // JavaScript months are 0-indexed
245+
day,
246+
now.getHours(),
247+
now.getMinutes(),
248+
now.getSeconds(),
249+
now.getMilliseconds(),
250+
);
238251
const backendPayload = {
239252
id: globalThis.crypto.randomUUID(), // Generate client-side ID
240253
user_id: userId,
@@ -244,7 +257,7 @@ export class TransactionService {
244257
description: formData.description,
245258
merchant_name: formData.merchant || 'Unknown Merchant',
246259
merchant_category: formData.category,
247-
transaction_date: new Date(formData.date).toISOString(),
260+
transaction_date: selectedDate.toISOString(),
248261
transaction_type: formData.type === 'credit' ? 'REFUND' : 'PURCHASE',
249262
status: 'PENDING',
250263
// Optional fields

podman-compose.yml

Lines changed: 5 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ services:
99
container_name: postgres
1010
env_file:
1111
- .env.development
12-
environment:
13-
POSTGRES_DB: ${POSTGRES_DB:-spending-monitor}
14-
POSTGRES_USER: ${POSTGRES_USER:-user}
15-
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password}
1612
ports:
1713
- "${DB_PORT:-5432}:5432"
1814
volumes:
@@ -54,9 +50,8 @@ services:
5450
platform: linux/amd64
5551
container_name: spending-monitor-keycloak-setup
5652
working_dir: /app
57-
environment:
58-
- KEYCLOAK_URL=http://keycloak:8080
59-
- DATABASE_URL=postgresql+asyncpg://user:password@postgres:5432/spending-monitor
53+
env_file:
54+
- .env.development
6055
volumes:
6156
- ./packages/auth/scripts:/app/scripts
6257
- ./data:/app/data
@@ -69,7 +64,7 @@ services:
6964
echo 'Keycloak port is open, waiting 20 more seconds for full startup...' &&
7065
sleep 20 &&
7166
pip install --quiet requests sqlalchemy asyncpg psycopg2-binary pyyaml &&
72-
python /app/scripts/setup_keycloak.py
67+
python /app/scripts/setup_keycloak_with_db_users.py
7368
"
7469
depends_on:
7570
- keycloak
@@ -113,30 +108,8 @@ services:
113108
container_name: spending-monitor-api
114109
env_file:
115110
- .env.development
116-
environment:
117-
- DATABASE_URL=${DATABASE_URL:-postgresql+asyncpg://user:password@postgres:5432/spending-monitor}
118-
- SMTP_HOST=${SMTP_HOST:-smtp4dev}
119-
- SMTP_PORT=${SMTP_PORT:-25}
120-
- SMTP_USERNAME=${SMTP_USERNAME:-}
121-
- SMTP_PASSWORD=${SMTP_PASSWORD:-}
122-
- SMTP_FROM_EMAIL=${SMTP_FROM_EMAIL:-spending-monitor@localhost}
123-
- SMTP_USE_TLS=${SMTP_USE_TLS:-false}
124-
- SMTP_USE_SSL=${SMTP_USE_SSL:-false}
125-
- ENVIRONMENT=${ENVIRONMENT:-development}
126-
- BYPASS_AUTH=${BYPASS_AUTH:-false}
127-
- API_PORT=${API_PORT:-8000}
128-
- CORS_ALLOWED_ORIGINS=${CORS_ALLOWED_ORIGINS:-http://localhost:3000,http://localhost:8080}
129-
- ALLOWED_ORIGINS=${ALLOWED_ORIGINS:-http://localhost:3000,http://localhost:8080}
130-
- ALLOWED_HOSTS=${ALLOWED_HOSTS:-http://localhost:5173}
131-
- BASE_URL=${BASE_URL:-https://api.openai.com/v1}
132-
- API_KEY=${API_KEY:-your-openai-api-key-here}
133-
- MODEL=${MODEL:-gpt-3.5-turbo}
134-
- LLAMASTACK_BASE_URL=${LLAMASTACK_BASE_URL:-http://llamastack:8321}
135-
- LLM_PROVIDER=${LLM_PROVIDER:-openai}
136-
- NODE_ENV=${NODE_ENV:-development}
137-
- KEYCLOAK_URL=${KEYCLOAK_URL:-http://keycloak:8080}
138-
- KEYCLOAK_REALM=${KEYCLOAK_REALM:-spending-monitor}
139-
- KEYCLOAK_CLIENT_ID=${KEYCLOAK_CLIENT_ID:-spending-monitor}
111+
112+
140113
ports:
141114
- "8000:8000"
142115
depends_on:
@@ -165,13 +138,6 @@ services:
165138
- api
166139
networks:
167140
- spending-monitor
168-
environment:
169-
- VITE_API_BASE_URL=${VITE_API_BASE_URL:-/api}
170-
- VITE_BYPASS_AUTH=${VITE_BYPASS_AUTH:-false}
171-
- VITE_ENVIRONMENT=${VITE_ENVIRONMENT:-staging}
172-
# UI needs public Keycloak URL (accessible from browser), not internal container hostname
173-
- VITE_KEYCLOAK_URL=${VITE_KEYCLOAK_URL:-http://localhost:8080/realms/spending-monitor}
174-
- VITE_KEYCLOAK_CLIENT_ID=${VITE_KEYCLOAK_CLIENT_ID:-spending-monitor}
175141

176142
# Nginx reverse proxy to serve UI and proxy API
177143
nginx:

0 commit comments

Comments
 (0)