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
64 changes: 64 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<!--
PR title MUST be a Conventional Commits subject (see CONTRIBUTING.md).
Examples:
feat(frontend): …
fix(backend): …
docs(repo): …
-->

## Summary

<!-- One or two sentences. What does this PR change, and why does it matter? -->

## Motivation

<!-- The problem, ticket, incident, or user feedback that prompted the change. -->

## Changes

<!-- Bullet list of what was actually modified. Group by side: frontend / backend / repo. -->

-

## Screenshots

<!--
REQUIRED for any frontend visual change. Drop before/after pairs of every
screen the change touches. Skip this section ONLY for backend / docs / CI PRs.
-->

## Test plan

<!--
How did you verify the change locally? Commands run + manual smoke walk.
Examples:
- [ ] `cd frontend && npm run lint`
- [ ] `cd frontend && npm run test`
- [ ] `cd backend && pytest tests/ -v`
- [ ] Manual: uploaded sample-data3.csv, ran 3 rows with concurrency 2
-->

- [ ]

## Versioning

<!-- Tick all that apply. -->

- [ ] No version bump (chore / docs / refactor / test only)
- [ ] Frontend bump (`frontend/package.json`) — new version: `_._._`
- [ ] Backend bump (`backend/API/api.py` `version` literal) — new version: `_._._`
- [ ] `CHANGELOG.md` updated in this PR

## Breaking changes

<!-- Anything that downstream consumers (API callers, profile JSON readers) must adapt to. Default: None. -->

None.

## Checklist

- [ ] PR title follows Conventional Commits
- [ ] Branch name follows `<type>/<short-description>`
- [ ] Local lint + tests pass for the side(s) I changed
- [ ] No `.env` / API keys / secrets committed
- [ ] Screenshots included for frontend visual changes
25 changes: 25 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,28 @@ jobs:
- name: Run pytest
working-directory: backend
run: pytest tests/ -v

frontend:
name: Frontend lint + vitest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Node.js 20
uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
cache-dependency-path: frontend/package-lock.json

- name: Install dependencies
working-directory: frontend
run: npm ci

- name: Lint
working-directory: frontend
run: npm run lint

- name: Run vitest
working-directory: frontend
run: npm run test
77 changes: 77 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Changelog

All notable changes to this project are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

The repository ships as one product. `frontend` (`frontend/package.json`) and
`backend` (`backend/API/api.py`) share a single version and bump together
(lockstep), even when only one side changes in a release. Tags use the form
`v<version>`.

## [Unreleased]

## [1.1.0] - 2026-05-06

### Changed

- Visual redesign of the frontend to a Stripe Dashboard-inspired aesthetic:
full-bleed hero header with brand mark + live backend status chip, layered
duotone shadows, Stripe purple primary (`#635bff`) on a faint cool-purple
page wash, KPI stat-card row above the results table, numbered step badges
(1–4) on every workflow section, Bot icon brand mark.
- Introduced a CSS-variable design-token system in
`frontend/app/globals.css` (`--background`, `--surface`, `--foreground`,
`--primary`, status colors, radii, layered shadows, `.step-badge`,
`.hero-backdrop`, `.eyebrow`, status-dot utilities). Every component now
consumes tokens, so a future palette swap is a one-place edit.
- Typography ladder: 15px html base, h1 ~30px bold tracking-tight, h3
base-weight semibold, tabular-nums on every number column / counter / row
index / progress percentage.

### Added

- Vitest + React Testing Library frontend test harness — first 5 suites
(40 tests) covering `profileUtils` (including the load-bearing legacy
profile migration), `csvParser`, `ColumnTags`, `OutputSchemaBuilder`, and
the `ResultsTable` schema-derived columns regression guard.
- `frontend` job added to `.github/workflows/test.yml` (lint + vitest,
parallel to the existing backend pytest job).
- `BackendStatusChip` in the hero — pulls `model` and `llm_profile` from
`/api/agent-status` and renders a live "Connected · local · gemma-4-31B-it"
pill. Falls back to "Offline" if the status fetch fails.
- README tutorial video embed at the top
([youtu.be/wnuQufwRAXE](https://youtu.be/wnuQufwRAXE)).
- Repo conventions (first time, since the repo just went public):
`CHANGELOG.md`, `CONTRIBUTING.md`, `SECURITY.md`,
`.github/PULL_REQUEST_TEMPLATE.md`.

### Fixed

- `FileUpload`: the entire dashed drop zone is now clickable. Previously
only the inline "Click to upload" link triggered the file picker; the
rest of the box did nothing despite visually inviting clicks. Outer div
is now `role="button"` with full-area click + Enter/Space keyboard
activation.

### Unchanged (intentionally)

- All HTTP API contracts (request / response shapes, defaults, error shape,
`submit_result` tool-call protocol) are identical to v1.0.0.
- All component props, JSX layout / order, state shape, and runtime
behavior are unchanged. The redesign is a className / CSS-variable /
additive-element pass — no behavioral surface moved.
- Worker-pool concurrency, profile JSON shape, and CSV parsing all behave
identically.

## [1.0.0] - 2026-04-XX — Initial public release

### Added

- Initial public Apache-2.0 release of Knowledge Robot — Next.js frontend +
Python Flask backend agentic AI for repetitive knowledge-work tasks.

[Unreleased]: https://github.com/dimknaf/knowledge-robot/compare/v1.1.0...HEAD
[1.1.0]: https://github.com/dimknaf/knowledge-robot/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/dimknaf/knowledge-robot/releases/tag/v1.0.0
74 changes: 74 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Contributing to Knowledge Robot

Thanks for taking the time to contribute. This document captures the small set of
conventions we follow so that the public history stays readable.

## Branching

- The default branch is `master`.
- Branch off `master` for every change. Branch names follow the pattern
`<type>/<short-description>`, e.g. `feat/csv-multi-row-paste`,
`fix/firecrawl-truncation`, `docs/security-policy`,
`redesign/professional-ui-v1.1`.

## Commit messages — Conventional Commits

We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
Each commit subject is one of:

- `feat(scope): …` — user-visible feature change
- `fix(scope): …` — bug fix
- `docs(scope): …` — documentation only
- `refactor(scope): …` — code change, no behavior change
- `test(scope): …` — tests only
- `chore(scope): …` — tooling, deps, repo hygiene
- `ci(scope): …` — CI/CD configuration
- `perf(scope): …` — performance improvement

`scope` is optional but encouraged. Use `frontend`, `backend`, `repo`, or a
component name (e.g. `feat(frontend/results-table): …`).

## Pull requests

- One logical change per PR. Use squash-merge.
- The PR title MUST be a Conventional Commits subject — that line becomes the
squash-merged commit on `master`.
- Use `.github/PULL_REQUEST_TEMPLATE.md`. Frontend visual changes require
before/after screenshots.
- Both CI jobs (`backend` pytest, `frontend` lint+test) must be green before
merge.

## Versioning — single shared version (lockstep)

The repository ships as one product. `frontend` and `backend` carry the
**same version number** and bump together (lockstep), even when only one side
changes in a given release.

- Frontend version lives in `frontend/package.json`.
- Backend version is the literal in `backend/API/api.py` (the `version` field
of the `/health` response).

Both must be updated to the same value in the same PR that introduces the
release. Tags use the form `v<version>` (e.g. `v1.1.0`). Update
`CHANGELOG.md` in the same PR.

## Local checks before opening a PR

```powershell
# Frontend
cd frontend
npm run lint
npm run test
npm run build

# Backend
cd backend
pytest tests/ -v
```

If you cannot run one half (e.g. you only touched the frontend), say so in the
PR's Test Plan and lean on CI to cover the other side.

## Reporting a vulnerability

See `SECURITY.md`.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Knowledge Robot

[![Knowledge Robot — tutorial video](https://img.youtube.com/vi/wnuQufwRAXE/maxresdefault.jpg)](https://youtu.be/wnuQufwRAXE "Watch the Knowledge Robot tutorial on YouTube")

> **▶ Watch the tutorial:** [youtu.be/wnuQufwRAXE](https://youtu.be/wnuQufwRAXE) — 2-minute walkthrough of upload → prompt → schema → run → export.

An agentic AI that automates the repetitive work knowledge workers do every day — web research, browsing, data extraction, and structured note-taking. You describe the task once, define the shape of the output you want, and the agent runs it for you over many inputs: searching the web, visiting pages, and returning typed results that match your schema.

- **Agent runtime** — Flask + the OpenAI Agents SDK, LiteLLM-driven and profile-pluggable so you can switch providers with one env var
Expand Down
29 changes: 29 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Security policy

## Reporting a vulnerability

If you believe you have found a security vulnerability in Knowledge Robot,
please report it privately rather than opening a public issue.

Email the maintainer with the details: please include reproduction steps, the
affected component (frontend / backend / docker image), and any logs or
proof-of-concept inputs you have.

You should expect an acknowledgement within a few business days and a fix or
mitigation timeline shortly after triage. We will credit reporters in the
release notes unless they request otherwise.

## Scope

In scope:

- Code in this repository (frontend, backend, docker compose files).
- Default configuration shipped in `.env.local.example`.

Out of scope:

- Third-party LLM provider behavior (DeepInfra, Gemini, Firecrawl, etc.) — please
report those upstream.
- User-supplied API keys leaking via misconfigured deployments — that is a
deployment concern, not a code defect, but we are happy to help harden the
default configuration if you find a weak point.
2 changes: 1 addition & 1 deletion backend/API/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def health_check():
return jsonify({
'status': 'healthy',
'service': 'knowledge-robot',
'version': '1.0.0',
'version': '1.1.0',
'agent_ready': status['litellm_model_initialized'],
'model': status['model'],
})
Expand Down
56 changes: 56 additions & 0 deletions frontend/__tests__/ColumnTags.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { describe, it, expect, vi, afterEach } from 'vitest';
import { render, screen, cleanup } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import ColumnTags from '@/components/ColumnTags';

afterEach(() => {
cleanup();
});

describe('ColumnTags', () => {
it('renders one button per column', () => {
render(
<ColumnTags columns={['company', 'country', 'revenue']} onTagClick={vi.fn()} />
);

expect(screen.getByRole('button', { name: /company/ })).toBeInTheDocument();
expect(screen.getByRole('button', { name: /country/ })).toBeInTheDocument();
expect(screen.getByRole('button', { name: /revenue/ })).toBeInTheDocument();
});

it('shows the singular "column" label when count is 1', () => {
render(<ColumnTags columns={['only']} onTagClick={vi.fn()} />);
expect(screen.getByText(/^1 column$/)).toBeInTheDocument();
});

it('shows the plural "columns" label when count > 1', () => {
render(<ColumnTags columns={['a', 'b', 'c']} onTagClick={vi.fn()} />);
expect(screen.getByText(/^3 columns$/)).toBeInTheDocument();
});

it('calls onTagClick with the column name when a chip is clicked', async () => {
const user = userEvent.setup();
const handleClick = vi.fn();

render(
<ColumnTags columns={['company', 'country']} onTagClick={handleClick} />
);

await user.click(screen.getByRole('button', { name: /company/ }));
expect(handleClick).toHaveBeenCalledTimes(1);
expect(handleClick).toHaveBeenCalledWith('company');

await user.click(screen.getByRole('button', { name: /country/ }));
expect(handleClick).toHaveBeenCalledTimes(2);
expect(handleClick).toHaveBeenLastCalledWith('country');
});

it('renders the step badge "1" indicating the Data step', () => {
const { container } = render(
<ColumnTags columns={['x']} onTagClick={vi.fn()} />
);
const badge = container.querySelector('.step-badge');
expect(badge).not.toBeNull();
expect(badge?.textContent).toBe('1');
});
});
Loading
Loading