Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 27 additions & 2 deletions .github/workflows/github_ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ permissions:
contents: read

jobs:
build:
build-test-chatbot:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -44,4 +44,29 @@ jobs:
hatch run dev:typecheck
- name: Test
run: |
hatch run dev:test
hatch run dev:test
build-test-ui:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js 20
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install dependencies
run: |
npm install --legacy-peer-deps
working-directory: ./ui
- name: Lint
run: |
npm run lint
working-directory: ./ui
- name: Format
run: |
npm run format
working-directory: ./ui
- name: Test
run: |
npm run test_component
npm run test_e2e
working-directory: ./ui
2 changes: 2 additions & 0 deletions api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
app = FastAPI()

origins = [
"http://127.0.0.1:3000",
"http://localhost:3000",
"http://ui:3000",
]

app.add_middleware(
Expand Down
6 changes: 6 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,9 @@ services:
image: chatbot-api:latest
ports:
- "8080:8080"
ui:
image: chatbot-ui:latest
ports:
- "3000:3000"
environment:
- HOSTNAME=0.0.0.0
2 changes: 1 addition & 1 deletion src/chatbot/__about__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2025-present Nam Le <lehainam2371999@gmail.com>
#
# SPDX-License-Identifier: MIT
__version__ = "0.0.7"
__version__ = "0.0.8"
6 changes: 6 additions & 0 deletions ui/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 80
}
66 changes: 66 additions & 0 deletions ui/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# syntax=docker.io/docker/dockerfile:1

FROM node:18-alpine AS base

# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app

# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi


# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED=1

RUN \
if [ -f yarn.lock ]; then yarn run build; \
elif [ -f package-lock.json ]; then npm run build; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
else echo "Lockfile not found." && exit 1; \
fi

# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app

ENV NODE_ENV=production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED=1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT=3000

# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/config/next-config-js/output
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
102 changes: 51 additions & 51 deletions ui/app/components/HumanReviewForm.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,58 @@
import React from "react";
import { HumanReview } from "../page";
import React from 'react';
import { HumanReview } from '../models';

interface HumanReviewFormProps {
searchQuery: string;
humanReview: HumanReview;
setHumanReview: (review: HumanReview) => void;
sendHumanReview: () => void;
searchQuery: string;
humanReview: HumanReview;
setHumanReview: (review: HumanReview) => void;
sendHumanReview: () => void;
}

export default function HumanReviewForm({
searchQuery,
humanReview,
setHumanReview,
sendHumanReview,
searchQuery,
humanReview,
setHumanReview,
sendHumanReview,
}: HumanReviewFormProps) {
return (
<div className="flex flex-col items-center gap-4">
{/* Display the search query */}
<p className="text-center">
Chat model wants to search using this query:{" "}
<span className="font-bold">{searchQuery}</span>
</p>
return (
<div className="flex flex-col items-center gap-4">
{/* Display the search query */}
<p className="text-center">
Chat model wants to search using this query:{' '}
<span className="font-bold">{searchQuery}</span>
</p>

{/* Human review form */}
<div className="flex flex-col items-center gap-2">
<select
value={humanReview.action}
onChange={(e) =>
setHumanReview(new HumanReview(e.target.value, humanReview.data))
}
className="border p-2 w-80"
>
<option value="continue">Continue</option>
<option value="feedback">Feedback</option>
</select>
{humanReview.action === "feedback" && (
<textarea
placeholder="Enter feedback"
value={humanReview.data}
onChange={(e) =>
setHumanReview(
new HumanReview(humanReview.action, e.target.value)
)
}
className="border p-2 w-80 h-20"
/>
)}
<button
onClick={sendHumanReview}
className="px-4 py-2 bg-green-500 text-white rounded"
>
Submit Human Review
</button>
</div>
</div>
);
}
{/* Human review form */}
<div className="flex flex-col items-center gap-2">
<select
value={humanReview.action}
onChange={(e) =>
setHumanReview(new HumanReview(e.target.value, humanReview.data))
}
className="border p-2 w-80"
>
<option value="continue">Continue</option>
<option value="feedback">Feedback</option>
</select>
{humanReview.action === 'feedback' && (
<textarea
placeholder="Enter feedback"
value={humanReview.data}
onChange={(e) =>
setHumanReview(
new HumanReview(humanReview.action, e.target.value)
)
}
className="border p-2 w-80 h-20"
/>
)}
<button
onClick={sendHumanReview}
className="px-4 py-2 bg-green-500 text-white rounded"
>
Submit Human Review
</button>
</div>
</div>
);
}
48 changes: 24 additions & 24 deletions ui/app/components/MessageInput.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
import React from "react";
import React from 'react';

interface MessageInputProps {
message: string;
setMessage: (message: string) => void;
sendMessage: () => void;
message: string;
setMessage: (message: string) => void;
sendMessage: () => void;
}

export default function MessageInput({
message,
setMessage,
sendMessage,
message,
setMessage,
sendMessage,
}: MessageInputProps) {
return (
<div className="flex flex-col items-center gap-2">
<textarea
placeholder="Enter your message"
value={message}
onChange={(e) => setMessage(e.target.value)}
className="border p-2 w-80 h-20"
/>
<button
onClick={sendMessage}
className="px-4 py-2 bg-green-500 text-white rounded"
>
Send Message
</button>
</div>
);
}
return (
<div className="flex flex-col items-center gap-2">
<textarea
placeholder="Enter your message"
value={message}
onChange={(e) => setMessage(e.target.value)}
className="border p-2 w-80 h-20"
/>
<button
onClick={sendMessage}
className="px-4 py-2 bg-green-500 text-white rounded"
>
Send Message
</button>
</div>
);
}
20 changes: 10 additions & 10 deletions ui/app/components/ResponseDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import React from "react";
import React from 'react';

interface ResponseDisplayProps {
response: string;
response: string;
}

export default function ResponseDisplay({ response }: ResponseDisplayProps) {
if (!response) return null;
if (!response) return null;

return (
<div className="mt-4 p-4 border rounded bg-gray-100">
<h2 className="font-bold text-gray-800">Response:</h2>
<p className="text-gray-800">{response}</p>
</div>
);
}
return (
<div className="mt-4 p-4 border rounded bg-gray-100">
<h2 className="font-bold text-gray-800">Response:</h2>
<p className="text-gray-800">{response}</p>
</div>
);
}
Loading