Skip to content
This repository was archived by the owner on Aug 22, 2025. It is now read-only.

Commit f4b140a

Browse files
jzbouriaaron-ridgescalvinxiang
committed
aws stuff and optimized sql queries
Co-authored-by: aaron-ridges <aaron@hiddenharvestventures.com> Co-authored-by: calvin688 <calvin@hiddenharvestventures.com>
1 parent 79b0558 commit f4b140a

8 files changed

Lines changed: 267 additions & 78 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Build & Deploy to AWS
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
7+
env:
8+
AWS_REGION: us-east-1
9+
ECR_REPO: 160684124315.dkr.ecr.us-east-1.amazonaws.com/ridges-api
10+
SERVICE_NAME: api
11+
ENV_NAME: prod
12+
13+
jobs:
14+
build-deploy:
15+
runs-on: ubuntu-latest
16+
17+
permissions:
18+
id-token: write # GitHub OIDC for AWS
19+
contents: read
20+
21+
steps:
22+
- name: Checkout repository
23+
uses: actions/checkout@v4
24+
25+
- name: Configure AWS credentials (OIDC)
26+
uses: aws-actions/configure-aws-credentials@v4
27+
with:
28+
role-to-assume: arn:aws:iam::160684124315:role/github-actions-ridges-api
29+
aws-region: ${{ env.AWS_REGION }}
30+
31+
- name: Set up Docker Buildx
32+
uses: docker/setup-buildx-action@v3
33+
34+
- name: Login to Amazon ECR
35+
uses: aws-actions/amazon-ecr-login@v2
36+
37+
- name: Derive image tag from commit SHA
38+
id: vars
39+
run: echo "TAG=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
40+
41+
- name: Build and push multi-architecture image
42+
run: |
43+
docker buildx build \
44+
--platform linux/amd64,linux/arm64 \
45+
-t $ECR_REPO:${{ steps.vars.outputs.TAG }} \
46+
-t $ECR_REPO:latest \
47+
--push .
48+
49+
- name: Install AWS Copilot CLI
50+
run: |
51+
sudo curl -Lo /usr/local/bin/copilot https://github.com/aws/copilot-cli/releases/latest/download/copilot-linux
52+
sudo chmod +x /usr/local/bin/copilot
53+
54+
- name: Deploy with AWS Copilot
55+
run: |
56+
copilot svc deploy \
57+
--name $SERVICE_NAME \
58+
--env $ENV_NAME \
59+
--tag ${{ steps.vars.outputs.TAG }}

Dockerfile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
FROM python:3.11-slim
2+
3+
# Prevent Python from writing .pyc files and buffering stdout/stderr
4+
ENV PYTHONDONTWRITEBYTECODE=1 \
5+
PYTHONUNBUFFERED=1
6+
7+
# Install OS packages (if psycopg2 wants build deps add them here)
8+
RUN apt-get update && apt-get install -y --no-install-recommends \
9+
build-essential \
10+
libpq-dev \
11+
&& rm -rf /var/lib/apt/lists/*
12+
13+
# Set workdir
14+
WORKDIR /app
15+
16+
# Install python dependencies first (leverages Docker layer caching)
17+
COPY requirements.txt ./
18+
RUN pip install --no-cache-dir -r requirements.txt
19+
20+
# Copy application code
21+
COPY . .
22+
23+
# Expose port expected by App Runner
24+
EXPOSE 8080
25+
26+
# Start the FastAPI app
27+
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8080"]

copilot/.workspace

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
application: ridges-api

copilot/api/manifest.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# The manifest for the "api" service.
2+
# Read the full specification for the "Load Balanced Web Service" type at:
3+
# https://aws.github.io/copilot-cli/docs/manifest/lb-web-service/
4+
5+
# Your service name will be used in naming your resources like log groups, ECS services, etc.
6+
name: api
7+
type: Load Balanced Web Service
8+
9+
# Distribute traffic to your service.
10+
http:
11+
# Requests to this path will be forwarded to your service.
12+
# To match all requests you can use the "/" path.
13+
path: '/'
14+
# You can specify a custom health check path. The default is "/".
15+
healthcheck: '/docs'
16+
17+
# Configuration for your containers and service.
18+
image:
19+
location: 160684124315.dkr.ecr.us-east-1.amazonaws.com/ridges-api:latest
20+
# Port exposed through your container to route traffic to it.
21+
port: 8080
22+
23+
cpu: 256 # Number of CPU units for the task.
24+
memory: 512 # Amount of memory in MiB used by the task.
25+
count: 1 # Number of tasks that should be running in your service.
26+
exec: true # Enable running commands in your container.
27+
network:
28+
connect: true # Enable Service Connect for intra-environment traffic between services.
29+
30+
# storage:
31+
# readonly_fs: true # Limit to read-only access to mounted root filesystems.
32+
33+
# Optional fields for more advanced use-cases.
34+
#
35+
#variables: # Pass environment variables as key value pairs.
36+
# LOG_LEVEL: info
37+
38+
#secrets: # Pass secrets from AWS Systems Manager (SSM) Parameter Store.
39+
# GITHUB_TOKEN: GITHUB_TOKEN # The key is the name of the environment variable, the value is the name of the SSM parameter.
40+
41+
# You can override any of the values defined above by environment.
42+
#environments:
43+
# test:
44+
# count: 2 # Number of tasks to run for the "test" environment.
45+
# deployment: # The deployment strategy for the "test" environment.
46+
# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments.
47+
48+
platform: linux/arm64
49+
50+
variables:
51+
AWS_RDS_PLATFORM_ENDPOINT: platform.cqnsk0q6war0.us-east-1.rds.amazonaws.com
52+
AWS_MASTER_USERNAME: ridges_master
53+
AWS_MASTER_PASSWORD: '>~AIQ?nLu>ksrI5rTpt[1gb:k.a|'
54+
AWS_RDS_PLATFORM_DB_NAME: platform_db
55+
PYTHONUNBUFFERED: '1'
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# The manifest for the "prod" environment.
2+
# Read the full specification for the "Environment" type at:
3+
# https://aws.github.io/copilot-cli/docs/manifest/environment/
4+
5+
# Your environment name will be used in naming your resources like VPC, cluster, etc.
6+
name: prod
7+
type: Environment
8+
9+
10+
# Import your own VPC and subnets or configure how they should be created.
11+
# network:
12+
# vpc:
13+
# id:
14+
15+
# Configure the load balancers in your environment, once created.
16+
# http:
17+
# public:
18+
# private:
19+
20+
# Configure observability for your environment resources.
21+
observability:
22+
container_insights: false

src/db/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class CodegenResponse(BaseModel):
3535
score: Optional[float] = None
3636
evaluated_at: Optional[datetime] = None
3737
response_patch: str
38+
response_count: Optional[int] = None
39+
average_score: Optional[float] = None
3840

3941
class RegressionResponse(BaseModel):
4042
challenge_id: str

src/db/operations.py

Lines changed: 89 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -308,38 +308,27 @@ def get_codegen_challenges(self, challenge_id: str = None) -> List[Dict]:
308308
print(f"Error getting codegen challenges: {str(e)}")
309309
return []
310310

311-
def get_codegen_challenge_responses(self, challenge_id: str = None, miner_hotkey: str = None) -> List[CodegenResponse]:
311+
def get_codegen_challenge_responses(self, challenge_id: str = None, miner_hotkey: str = None, min_score: float = 0, min_response_count: int = 0, sort_by_score: bool = False, max_miners: int = 5, hours: int = 24) -> List[Dict]:
312312
"""Retrieve codegen responses from the database (AWS Postgres RDS).
313-
Returns a list of CodegenResponse objects matching the original output format.
313+
Returns a list of dictionaries containing miner information and their responses.
314314
Only includes responses where evaluated is TRUE and score is not NULL.
315+
316+
Additional parameters:
317+
- min_score: Minimum average score for miners to be included
318+
- min_response_count: Minimum number of responses required per miner
319+
- sort_by_score: Whether to sort miners by average score
320+
- max_miners: Maximum number of miners to return
321+
- hours: Number of hours to look back (-1 for all time)
315322
"""
316323
try:
317324
with self.conn:
318325
with self.conn.cursor() as cursor:
319-
if challenge_id:
320-
cursor.execute("""
326+
# Base query with optimized structure
327+
base_query = """
328+
WITH RECURSIVE time_bucket AS (
321329
SELECT
322-
r.challenge_id,
323330
r.miner_hotkey,
324-
r.node_id,
325-
r.processing_time,
326-
r.received_at,
327-
r.completed_at,
328-
r.evaluated,
329-
r.score,
330-
r.evaluated_at,
331-
cr.response_patch
332-
FROM responses r
333-
JOIN codegen_responses cr
334-
ON r.challenge_id = cr.challenge_id
335-
AND r.miner_hotkey = cr.miner_hotkey
336-
WHERE r.challenge_id = %s AND r.evaluated = TRUE AND r.score IS NOT NULL
337-
""", (challenge_id,))
338-
elif miner_hotkey:
339-
cursor.execute("""
340-
SELECT
341331
r.challenge_id,
342-
r.miner_hotkey,
343332
r.node_id,
344333
r.processing_time,
345334
r.received_at,
@@ -352,43 +341,89 @@ def get_codegen_challenge_responses(self, challenge_id: str = None, miner_hotkey
352341
JOIN codegen_responses cr
353342
ON r.challenge_id = cr.challenge_id
354343
AND r.miner_hotkey = cr.miner_hotkey
355-
WHERE r.miner_hotkey = %s AND r.evaluated = TRUE AND r.score IS NOT NULL
356-
""", (miner_hotkey,))
344+
WHERE r.evaluated = TRUE
345+
AND r.score IS NOT NULL
346+
"""
347+
348+
# Add time filter if hours is not -1
349+
if hours != -1:
350+
base_query += " AND r.completed_at >= NOW() - INTERVAL '%s hours'"
351+
params = [hours]
357352
else:
358-
cursor.execute("""
353+
params = []
354+
355+
# Add challenge_id or miner_hotkey filter if provided
356+
if challenge_id:
357+
base_query += " AND r.challenge_id = %s"
358+
params.append(challenge_id)
359+
elif miner_hotkey:
360+
base_query += " AND r.miner_hotkey = %s"
361+
params.append(miner_hotkey)
362+
363+
base_query += """
364+
),
365+
miner_stats AS (
359366
SELECT
360-
r.challenge_id,
361-
r.miner_hotkey,
362-
r.node_id,
363-
r.processing_time,
364-
r.received_at,
365-
r.completed_at,
366-
r.evaluated,
367-
r.score,
368-
r.evaluated_at,
369-
cr.response_patch
370-
FROM responses r
371-
JOIN codegen_responses cr
372-
ON r.challenge_id = cr.challenge_id
373-
AND r.miner_hotkey = cr.miner_hotkey
374-
WHERE r.evaluated = TRUE AND r.score IS NOT NULL
375-
""")
367+
miner_hotkey,
368+
COUNT(*) as response_count,
369+
AVG(score) as average_score
370+
FROM time_bucket
371+
GROUP BY miner_hotkey
372+
HAVING COUNT(*) >= %s AND AVG(score) >= %s
373+
),
374+
miner_responses AS (
375+
SELECT
376+
t.miner_hotkey,
377+
json_agg(
378+
json_build_object(
379+
'challenge_id', t.challenge_id,
380+
'miner_hotkey', t.miner_hotkey,
381+
'node_id', t.node_id,
382+
'processing_time', t.processing_time,
383+
'received_at', t.received_at,
384+
'completed_at', t.completed_at,
385+
'evaluated', t.evaluated,
386+
'score', t.score,
387+
'evaluated_at', t.evaluated_at,
388+
'response_patch', t.response_patch
389+
)
390+
ORDER BY t.completed_at DESC
391+
) as responses
392+
FROM time_bucket t
393+
JOIN miner_stats ms ON t.miner_hotkey = ms.miner_hotkey
394+
GROUP BY t.miner_hotkey
395+
)
396+
SELECT
397+
mr.miner_hotkey,
398+
ms.response_count,
399+
ms.average_score,
400+
mr.responses
401+
FROM miner_responses mr
402+
JOIN miner_stats ms ON mr.miner_hotkey = ms.miner_hotkey
403+
"""
404+
405+
# Add final sorting
406+
if sort_by_score:
407+
base_query += " ORDER BY ms.average_score DESC, mr.miner_hotkey"
408+
else:
409+
base_query += " ORDER BY mr.miner_hotkey"
410+
411+
# Add final limit
412+
base_query += " LIMIT %s"
413+
params.extend([min_response_count, min_score, max_miners])
414+
415+
cursor.execute(base_query, params)
376416
rows = cursor.fetchall()
377417
if not rows:
378418
return []
419+
379420
return [
380-
CodegenResponse(
381-
challenge_id=row[0],
382-
miner_hotkey=row[1],
383-
node_id=row[2],
384-
processing_time=row[3],
385-
received_at=row[4],
386-
completed_at=row[5],
387-
evaluated=row[6],
388-
score=row[7],
389-
evaluated_at=row[8],
390-
response_patch=row[9]
391-
)
421+
{
422+
"miner_hotkey": row[0],
423+
"response_count": row[1],
424+
"average_score": row[2],
425+
"responses": [CodegenResponse(**response) for response in row[3]]
426+
}
392427
for row in rows
393428
]
394429
except Exception as e:

0 commit comments

Comments
 (0)