Skip to content

Commit 9d2d431

Browse files
committed
feat: implement AI service infrastructure, admin dashboard, and gRPC communication layer
1 parent 047740b commit 9d2d431

13 files changed

Lines changed: 66 additions & 18 deletions

File tree

.github/actions/setup-web/action.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ description: "Sets up node, restores cache, and installs dependencies for web jo
33
runs:
44
using: "composite"
55
steps:
6-
- uses: actions/checkout@v4
76
- name: Setup Node
87
uses: actions/setup-node@v4
98
with:

.github/workflows/ci.yml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,16 +186,18 @@ jobs:
186186
timeout-minutes: 10
187187
steps:
188188
- uses: actions/checkout@v4
189-
- name: Setup Python
189+
- name: Install uv
190+
uses: astral-sh/setup-uv@v5
191+
with:
192+
version: "0.5.x"
193+
- name: Set up Python
190194
uses: actions/setup-python@v5
191195
with:
192-
python-version: "3.12"
193-
- name: Install uv
194-
run: python -m pip install --upgrade pip uv
196+
python-version-file: "apps/ai-service/.python-version"
195197
- name: Sync dependencies with uv
196-
run: uv sync --project apps/ai-service --python 3.12 --group dev
198+
run: uv sync --project apps/ai-service --group dev
197199
- name: Run pytest
198-
run: uv run --project apps/ai-service --python 3.12 pytest -q
200+
run: uv run --project apps/ai-service pytest -q
199201

200202
secrets-scan:
201203
runs-on: ubuntu-latest

.gitignore

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,13 @@ error.txt
5757
doc/papers/
5858

5959
# Root-level Playwright dependencies
60-
/node_modules/
60+
/node_modules/
61+
/package-lock.json
62+
63+
# CI / debug artefacts (never commit)
64+
ci_jobs.json
65+
ci_status*.json
66+
failed_logs.txt
67+
gh_log*.txt
68+
mvn_test_err.txt
69+
*_jobs.json

apps/ai-service/ai_service/app.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ async def lifespan(_: FastAPI):
5252
serve_grpc(llm_service, kb_service, worker, idempotency_store, settings)
5353
)
5454

55+
def _grpc_err(t: asyncio.Task) -> None:
56+
if not t.cancelled() and t.exception():
57+
logger.error("gRPC server failed: %s", t.exception())
58+
59+
grpc_task.add_done_callback(_grpc_err)
60+
5561
yield
5662

5763
await llm_service.aclose()

apps/ai-service/ai_service/llm.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,16 +308,23 @@ async def embed(self, text: str) -> list[float]:
308308
if not cleaned:
309309
return [0.0 for _ in range(self.settings.embedding_dim)]
310310

311+
embed_timeout = 30.0
311312
errors: list[str] = []
312313
try:
313-
vector = await self._ollama.embed(cleaned, self.settings.ollama_embed_model)
314+
vector = await asyncio.wait_for(
315+
self._ollama.embed(cleaned, self.settings.ollama_embed_model),
316+
timeout=embed_timeout,
317+
)
314318
return self._normalize_dimension(vector)
315319
except Exception as ex:
316320
errors.append(f"ollama:{ex}")
317321

318322
if self.settings.openai_api_key:
319323
try:
320-
vector = await self._openai.embed(cleaned, self.settings.openai_embed_model)
324+
vector = await asyncio.wait_for(
325+
self._openai.embed(cleaned, self.settings.openai_embed_model),
326+
timeout=embed_timeout,
327+
)
321328
return self._normalize_dimension(vector)
322329
except Exception as ex:
323330
errors.append(f"openai:{ex}")

apps/api/pom.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,6 @@
145145
<artifactId>jakarta.annotation-api</artifactId>
146146
<version>3.0.0</version>
147147
</dependency>
148-
<dependency>
149-
<groupId>javax.annotation</groupId>
150-
<artifactId>javax.annotation-api</artifactId>
151-
<version>1.3.2</version>
152-
</dependency>
153148
</dependencies>
154149

155150
<build>

apps/api/src/main/java/com/edunexus/api/config/GrpcServerRunner.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public GrpcServerRunner(
3030
public void start() {
3131
try {
3232
server = ServerBuilder.forPort(port).addService(jobStatusService).build().start();
33-
log.info("gRPC Server started, listening on " + port);
33+
log.info("gRPC Server started, listening on {}", port);
3434
isRunning = true;
3535
} catch (IOException e) {
3636
log.error("Failed to start gRPC server", e);
@@ -42,6 +42,14 @@ public void start() {
4242
public void stop() {
4343
if (server != null) {
4444
server.shutdown();
45+
try {
46+
if (!server.awaitTermination(10, java.util.concurrent.TimeUnit.SECONDS)) {
47+
server.shutdownNow();
48+
}
49+
} catch (InterruptedException e) {
50+
server.shutdownNow();
51+
Thread.currentThread().interrupt();
52+
}
4553
}
4654
isRunning = false;
4755
}

apps/api/src/main/java/com/edunexus/api/service/AiClient.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,15 @@ public AiClient(
114114
@PreDestroy
115115
public void shutdown() {
116116
if (grpcChannel != null) {
117-
grpcChannel.shutdownNow();
117+
grpcChannel.shutdown();
118+
try {
119+
if (!grpcChannel.awaitTermination(5, TimeUnit.SECONDS)) {
120+
grpcChannel.shutdownNow();
121+
}
122+
} catch (InterruptedException e) {
123+
grpcChannel.shutdownNow();
124+
Thread.currentThread().interrupt();
125+
}
118126
}
119127
}
120128

apps/api/src/main/resources/application.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ app:
3232
refresh-token-expires-in: ${REFRESH_TOKEN_EXPIRES_IN:14d}
3333
ai-service-grpc-host: ${AI_SERVICE_GRPC_HOST:127.0.0.1}
3434
ai-service-grpc-port: ${AI_SERVICE_GRPC_PORT:50051}
35+
ai-service-http-host: ${AI_SERVICE_HOST:127.0.0.1}
36+
ai-service-http-port: ${AI_SERVICE_PORT:8000}
3537
ai-service-token: ${AI_SERVICE_TOKEN:change-this-in-local-too}
3638
ai-question-timeout-seconds: ${AI_QUESTION_TIMEOUT_SECONDS:180}
3739
lesson-plan-timeout-seconds: ${LESSON_PLAN_TIMEOUT_SECONDS:90}

apps/web/src/pages/admin/AdminDashboardPage.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ onMounted(loadMetrics);
528528
display: flex;
529529
gap: 14px;
530530
align-items: center;
531+
margin-top: 0 !important;
531532
}
532533
533534
.metric-icon {

0 commit comments

Comments
 (0)