Skip to content

Commit

Permalink
Merge branch 'dev' of https://github.com/PromptSail/prompt_sail into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
sliwaszymon committed Jul 9, 2024
2 parents d548237 + 9afdc00 commit daf0c3a
Show file tree
Hide file tree
Showing 17 changed files with 798 additions and 206 deletions.
138 changes: 101 additions & 37 deletions backend/src/app/web_api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import json
import re
from collections import defaultdict
from typing import Annotated, Any
Expand All @@ -18,11 +17,14 @@
CreateProjectSchema,
GetAIProviderPriceSchema,
GetAIProviderSchema,
GetCostPerTagSchema,
GetPortfolioDetailsSchema,
GetProjectPortfolioCostPerTagSchema,
GetProjectPortfolioSchema,
GetProjectSchema,
UpdateProjectSchema, GetProjectPortfolioCostPerTagSchema, GetCostPerTagSchema, GetProjectsUsageInTimeSchema,
GetProjectsUsageInTimeSchema,
GetProjectUsageSchema,
UpdateProjectSchema,
)
from projects.use_cases import (
add_project,
Expand Down Expand Up @@ -630,8 +632,10 @@ async def get_transaction_latency_statistics_over_time(
return new_stats


@app.get("/api/portfolio/details")
async def get_portfolio_details(ctx: Annotated[TransactionContext, Depends(get_transaction_context)]) -> GetPortfolioDetailsSchema:
@app.get("/api/portfolio/details", response_class=JSONResponse, dependencies=[Security(decode_and_validate_token)])
async def get_portfolio_details(
ctx: Annotated[TransactionContext, Depends(get_transaction_context)]
) -> GetPortfolioDetailsSchema:
project_count = ctx.call(count_projects)
total_cost_per_project, total_transactions_per_project = {}, {}
total_cost, total_transactions = 0, 0
Expand All @@ -641,7 +645,12 @@ async def get_portfolio_details(ctx: Annotated[TransactionContext, Depends(get_t
projects_ids = [project.id for project in projects]
for idx in projects_ids:
transactions = ctx.call(get_transactions_for_project, project_id=idx)
cost = sum([transaction.total_cost if transaction.total_cost is not None else 0 for transaction in transactions])
cost = sum(
[
transaction.total_cost if transaction.total_cost is not None else 0
for transaction in transactions
]
)
total_cost_per_project[idx] = cost
total_cost += cost
count = len(transactions)
Expand All @@ -652,12 +661,14 @@ async def get_portfolio_details(ctx: Annotated[TransactionContext, Depends(get_t
**project.model_dump(),
total_transactions=total_transactions_per_project[project.id],
total_cost=total_cost_per_project[project.id],
)
)
for project in projects
]
return GetPortfolioDetailsSchema(
total_cost=total_cost, total_transactions=total_transactions, projects=projects
)


@app.get(
"/api/portfolio/usage_in_time",
response_class=JSONResponse,
Expand All @@ -677,11 +688,13 @@ async def get_portfolio_usage_in_time(
project_ids = [project.id for project in projects]
for idx in project_ids:
if not date_from:
date_from = [project.created_at for project in projects if project.id == idx][0]
date_from = [
project.created_at for project in projects if project.id == idx
][0]
if not date_to:
date_to = datetime.now()
date_from, date_to = utils.check_dates_for_statistics(date_from, date_to)

count = ctx.call(
count_transactions,
project_id=idx,
Expand All @@ -692,7 +705,13 @@ async def get_portfolio_usage_in_time(
if count == 0:
results[idx] = []
else:
transactions = ctx.call(get_list_of_filtered_transactions, project_id=idx, date_from=date_from, date_to=date_to, status_codes=[200])
transactions = ctx.call(
get_list_of_filtered_transactions,
project_id=idx,
date_from=date_from,
date_to=date_to,
status_codes=[200],
)
transactions = [
StatisticTransactionSchema(
project_id=idx,
Expand All @@ -713,32 +732,39 @@ async def get_portfolio_usage_in_time(
)
for transaction in transactions
]

stats = utils.token_counter_for_transactions(
transactions, period, date_from, date_to
transactions, period, date_from, date_to, False
)
results[idx] = stats

aggregated_data = defaultdict(lambda: defaultdict(lambda: defaultdict(int)))

for project_id, records in results.items():
if len(records) > 0:
if len(records) > 0:
for record in records:
date = record.date
aggregated_data[date][project_id]["total_input_tokens"] += record.total_input_tokens
aggregated_data[date][project_id]["total_output_tokens"] += record.total_output_tokens
aggregated_data[date][project_id]["input_cumulative_total"] += record.input_cumulative_total
aggregated_data[date][project_id]["output_cumulative_total"] += record.output_cumulative_total
aggregated_data[date][project_id]["total_transactions"] += record.total_transactions
aggregated_data[date][project_id][
"total_input_tokens"
] += record.total_input_tokens
aggregated_data[date][project_id][
"total_output_tokens"
] += record.total_output_tokens
aggregated_data[date][project_id][
"input_cumulative_total"
] += record.input_cumulative_total
aggregated_data[date][project_id][
"output_cumulative_total"
] += record.output_cumulative_total
aggregated_data[date][project_id][
"total_transactions"
] += record.total_transactions
aggregated_data[date][project_id]["total_cost"] += record.total_cost

result = []

for date, statistics in aggregated_data.items():
date_record = {
"date": date,
"records": []
}
date_record = {"date": date, "records": []}
for project_id, stats in statistics.items():
record = {
"project_id": project_id,
Expand All @@ -747,17 +773,25 @@ async def get_portfolio_usage_in_time(
"input_cumulative_total": stats["input_cumulative_total"],
"output_cumulative_total": stats["output_cumulative_total"],
"total_transactions": stats["total_transactions"],
"total_cost": stats["total_cost"]
"total_cost": stats["total_cost"],
}
date_record["records"].append(record)
result.append(date_record)

for usage in result:
records = []
for record in usage['records']:
project_name = [project.name for project in projects if project.id == record["project_id"]][0]
records.append(GetProjectUsageSchema(**record, project_name=project_name))
projects_usage_in_time.append(GetProjectsUsageInTimeSchema(date=usage["date"], records=records))
for record in usage["records"]:
project_name = [
project.name
for project in projects
if project.id == record["project_id"]
][0]
records.append(
GetProjectUsageSchema(**record, project_name=project_name)
)
projects_usage_in_time.append(
GetProjectsUsageInTimeSchema(date=usage["date"], records=records)
)

return projects_usage_in_time

Expand All @@ -779,29 +813,59 @@ async def get_portfolio_costs_by_tag(
if len(transaction.tags) > 0:
for tag in transaction.tags:
try:
tags[tag] += transaction.total_cost if transaction.total_cost is not None else 0
tags[tag] += (
transaction.total_cost
if transaction.total_cost is not None
else 0
)
except KeyError:
tags[tag] = transaction.total_cost if transaction.total_cost is not None else 0

tags[tag] = (
transaction.total_cost
if transaction.total_cost is not None
else 0
)

try:
idx = cost_by_tag[date].index([stat for stat in cost_by_tag[date] if stat["tag"] == tag][0])
cost_by_tag[date][idx]["cost"] += transaction.total_cost if transaction.total_cost is not None else 0
idx = cost_by_tag[date].index(
[stat for stat in cost_by_tag[date] if stat["tag"] == tag][0]
)
cost_by_tag[date][idx]["cost"] += (
transaction.total_cost
if transaction.total_cost is not None
else 0
)
except IndexError:
cost_by_tag[date].append({"tag": tag, "cost": tags[tag]})
else:
tags["untagged-transactions"] += transaction.total_cost if transaction.total_cost is not None else 0
tags["untagged-transactions"] += (
transaction.total_cost if transaction.total_cost is not None else 0
)
try:
idx = cost_by_tag[date].index([stat for stat in cost_by_tag[date] if stat["tag"] == "untagged-transactions"][0])
cost_by_tag[date][idx]["cost"] += transaction.total_cost if transaction.total_cost is not None else 0
idx = cost_by_tag[date].index(
[
stat
for stat in cost_by_tag[date]
if stat["tag"] == "untagged-transactions"
][0]
)
cost_by_tag[date][idx]["cost"] += (
transaction.total_cost if transaction.total_cost is not None else 0
)
except IndexError:
cost_by_tag[date].append({"tag": "untagged-transactions", "cost": tags["untagged-transactions"]})

cost_by_tag[date].append(
{
"tag": "untagged-transactions",
"cost": tags["untagged-transactions"],
}
)

results = []
for key, values in cost_by_tag.items():
values = [GetCostPerTagSchema(**value) for value in values]
results.append(GetProjectPortfolioCostPerTagSchema(date=key, records=values))
return results


@app.get(
"/api/statistics/pricelist",
response_class=JSONResponse,
Expand Down
12 changes: 6 additions & 6 deletions backend/src/projects/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ class GetProjectPortfolioSchema(BaseModel):
tags: list[str] = 0
created_at: datetime
owner: str


class GetCostPerTagSchema(BaseModel):
tag: str
cost: int | float


class GetProjectUsageSchema(BaseModel):
project_name: str
project_id: str
Expand All @@ -82,8 +82,8 @@ class GetProjectUsageSchema(BaseModel):
output_cumulative_total: int
total_transactions: int
total_cost: int | float


class GetProjectsUsageInTimeSchema(BaseModel):
date: datetime
records: list[GetProjectUsageSchema]
Expand Down
4 changes: 3 additions & 1 deletion backend/src/transactions/use_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,9 @@ def store_transaction(
decoder = response._get_content_decoder()
buf = b"".join(buffer)

if "localhost" in str(request.__dict__["url"]) or "host.docker.internal" in str(request.__dict__["url"]):
if "localhost" in str(request.__dict__["url"]) or "host.docker.internal" in str(
request.__dict__["url"]
):
content = buf.decode("utf-8").split("\n")
rest, content = content[-2], content[:-2]
response_content = {
Expand Down
6 changes: 5 additions & 1 deletion backend/src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,7 @@ def token_counter_for_transactions(
period: str,
date_from: datetime | None = None,
date_to: datetime | None = None,
cumulative_total_cost: bool = True,
) -> list[GetTransactionUsageStatisticsSchema]:
"""
Calculate token usage statistics based on a given period.
Expand All @@ -1002,6 +1003,7 @@ def token_counter_for_transactions(
Choose from 'weekly', 'monthly' 'hourly', 'minutely' or 'daily'.
:param date_from: The starting date for the filter.
:param date_to: The ending date for the filter.
:param cumulative_total_cost: The switch that decides if total cost is cumulative or not.
:return: A list of GetTransactionUsageStatisticsSchema objects containing token usage statistics.
"""
data_dicts = [dto.model_dump() for dto in transactions]
Expand Down Expand Up @@ -1093,7 +1095,9 @@ def token_counter_for_transactions(
input_cumulative_total=data["input_cumulative_total"],
output_cumulative_total=data["output_cumulative_total"],
total_transactions=data["total_transactions"],
total_cost=data["total_cumulative_cost"],
total_cost=data["total_cumulative_cost"]
if cumulative_total_cost
else data["total_cost"],
)
for data in data_dicts
]
Expand Down
15 changes: 10 additions & 5 deletions ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"react-router-dom": "^6.20.0",
"react-svg": "^16.1.32",
"react-syntax-highlighter": "^15.5.0",
"recharts": "^2.12.3",
"recharts": "^2.13.0-alpha.4",
"sass": "^1.69.5",
"slugify": "^1.6.6",
"trendline": "^1.0.0",
Expand Down
2 changes: 2 additions & 0 deletions ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { getConfig } from './api/interfaces';
import { ErrorProvider } from './context/ErrorContext';
import LoginProvider from './context/LoginContext';
import Page404 from './components/errorPages/page404';
import Portfolio from './pages/Portfolio/Portfolio';

const App = () => {
const [isLogged, setLoginState] = useState(checkLogin());
Expand Down Expand Up @@ -57,6 +58,7 @@ const App = () => {
path="/transactions/:transactionId"
element={<Transaction />}
/>
<Route path="/portfolio" element={<Portfolio />} />
<Route path="*" element={<Page404 />} />
<Route path="/signin" element={<Navigate to="/" />} />
</Routes>
Expand Down
9 changes: 9 additions & 0 deletions ui/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ const api = {
},
getModels: (): Promise<AxiosResponse<getModels>> => {
return client.get('/api/statistics/pricelist');
},
getPortfolio: (): Promise<AxiosResponse<any>> => {
return client.get('/api/portfolio/details');
},
getPortfolio_projectsUsage: (params: string): Promise<AxiosResponse<any>> => {
return client.get(`/api/portfolio/usage_in_time${params}`);
},
getPortfolio_tagsUsage: (params: string): Promise<AxiosResponse<any>> => {
return client.get(`/api/portfolio/costs_by_tag${params}`);
}
};

Expand Down
Loading

0 comments on commit daf0c3a

Please sign in to comment.