Merge branch 'main' into chore/more-warnings
This commit is contained in:
2
.env.example
Normal file
2
.env.example
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# The location of the backend
|
||||||
|
VITE_API_BASE_URL=http://localhost:8000
|
||||||
@@ -28,6 +28,14 @@ npm run dev
|
|||||||
|
|
||||||
It should automatically reload when you save changes.
|
It should automatically reload when you save changes.
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
Copy `.env.example` to `.env.local` and adjust values as needed:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cp .env.example .env.local
|
||||||
|
```
|
||||||
|
|
||||||
## Git Hooks
|
## Git Hooks
|
||||||
|
|
||||||
To activate automatic linting, branch name checks and commit message checks, run:
|
To activate automatic linting, branch name checks and commit message checks, run:
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {useCallback, useEffect, useRef, useState} from "react";
|
|||||||
|
|
||||||
import {applyPriorityPredicates, type PriorityFilterPredicate} from "../../utils/priorityFiltering.ts";
|
import {applyPriorityPredicates, type PriorityFilterPredicate} from "../../utils/priorityFiltering.ts";
|
||||||
import {cell, type Cell} from "../../utils/cellStore.ts";
|
import {cell, type Cell} from "../../utils/cellStore.ts";
|
||||||
|
import { API_BASE_URL } from "../../config/api.ts";
|
||||||
|
|
||||||
type ExtraLevelName = 'LLM' | 'OBSERVATION' | 'ACTION' | 'CHAT';
|
type ExtraLevelName = 'LLM' | 'OBSERVATION' | 'ACTION' | 'CHAT';
|
||||||
|
|
||||||
@@ -210,7 +211,7 @@ export function useLogs(filterPredicates: Map<string, LogFilterPredicate>) {
|
|||||||
// Only create one SSE connection for the lifetime of the hook.
|
// Only create one SSE connection for the lifetime of the hook.
|
||||||
if (sseRef.current) return;
|
if (sseRef.current) return;
|
||||||
|
|
||||||
const es = new EventSource("http://localhost:8000/logs/stream");
|
const es = new EventSource(`${API_BASE_URL}/logs/stream`);
|
||||||
sseRef.current = es;
|
sseRef.current = es;
|
||||||
|
|
||||||
es.onmessage = (event) => {
|
es.onmessage = (event) => {
|
||||||
|
|||||||
11
src/config/api.ts
Normal file
11
src/config/api.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
declare const __VITE_API_BASE_URL__: string | undefined;
|
||||||
|
|
||||||
|
const DEFAULT_API_BASE_URL = "http://localhost:8000";
|
||||||
|
|
||||||
|
const rawApiBaseUrl =
|
||||||
|
(typeof __VITE_API_BASE_URL__ !== "undefined" ? __VITE_API_BASE_URL__ : undefined) ??
|
||||||
|
DEFAULT_API_BASE_URL;
|
||||||
|
|
||||||
|
export const API_BASE_URL = rawApiBaseUrl.endsWith("/")
|
||||||
|
? rawApiBaseUrl.slice(0, -1)
|
||||||
|
: rawApiBaseUrl;
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
// University within the Software Project course.
|
// University within the Software Project course.
|
||||||
// © Copyright Utrecht University (Department of Information and Computing Sciences)
|
// © Copyright Utrecht University (Department of Information and Computing Sciences)
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
import { API_BASE_URL } from '../../config/api.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays the current connection status of a robot in real time.
|
* Displays the current connection status of a robot in real time.
|
||||||
@@ -25,7 +26,7 @@ export default function ConnectedRobots() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Open a Server-Sent Events (SSE) connection to receive live ping updates.
|
// Open a Server-Sent Events (SSE) connection to receive live ping updates.
|
||||||
// We're expecting a stream of data like that looks like this: `data = False` or `data = True`
|
// We're expecting a stream of data like that looks like this: `data = False` or `data = True`
|
||||||
const eventSource = new EventSource("http://localhost:8000/robot/ping_stream");
|
const eventSource = new EventSource(`${API_BASE_URL}/robot/ping_stream`);
|
||||||
eventSource.onmessage = (event) => {
|
eventSource.onmessage = (event) => {
|
||||||
|
|
||||||
// Expecting messages in JSON format: `true` or `false`
|
// Expecting messages in JSON format: `true` or `false`
|
||||||
|
|||||||
@@ -2,16 +2,14 @@
|
|||||||
// University within the Software Project course.
|
// University within the Software Project course.
|
||||||
// © Copyright Utrecht University (Department of Information and Computing Sciences)
|
// © Copyright Utrecht University (Department of Information and Computing Sciences)
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
import { API_BASE_URL } from '../../config/api.ts';
|
||||||
const API_BASE = "http://localhost:8000";
|
|
||||||
const API_BASE_BP = API_BASE + "/button_pressed"; //UserInterruptAgent endpoint
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HELPER: Unified sender function
|
* HELPER: Unified sender function
|
||||||
*/
|
*/
|
||||||
export const sendAPICall = async (type: string, context: string, endpoint?: string) => {
|
export const sendAPICall = async (type: string, context: string, endpoint?: string) => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${API_BASE_BP}${endpoint ?? ""}`, {
|
const response = await fetch(`${API_BASE_URL}/button_pressed${endpoint ?? ""}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ type, context }),
|
body: JSON.stringify({ type, context }),
|
||||||
@@ -76,7 +74,7 @@ export function useExperimentLogger(onUpdate?: (data: ExperimentStreamData) => v
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("Connecting to Experiment Stream...");
|
console.log("Connecting to Experiment Stream...");
|
||||||
const eventSource = new EventSource(`${API_BASE}/experiment_stream`);
|
const eventSource = new EventSource(`${API_BASE_URL}/experiment_stream`);
|
||||||
|
|
||||||
eventSource.onmessage = (event) => {
|
eventSource.onmessage = (event) => {
|
||||||
try {
|
try {
|
||||||
@@ -112,7 +110,7 @@ export function useStatusLogger(onUpdate?: (data: ExperimentStreamData) => void
|
|||||||
}, [onUpdate]);
|
}, [onUpdate]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const eventSource = new EventSource(`${API_BASE}/status_stream`);
|
const eventSource = new EventSource(`${API_BASE_URL}/status_stream`);
|
||||||
eventSource.onmessage = (event) => {
|
eventSource.onmessage = (event) => {
|
||||||
try {
|
try {
|
||||||
const parsedData = JSON.parse(event.data);
|
const parsedData = JSON.parse(event.data);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import styles from './MonitoringPage.module.css';
|
import styles from './MonitoringPage.module.css';
|
||||||
import { sendAPICall } from './MonitoringPageAPI';
|
import { sendAPICall } from './MonitoringPageAPI';
|
||||||
|
import { API_BASE_URL } from '../../config/api.ts';
|
||||||
|
|
||||||
// --- GESTURE COMPONENT ---
|
// --- GESTURE COMPONENT ---
|
||||||
export const GestureControls: React.FC = () => {
|
export const GestureControls: React.FC = () => {
|
||||||
@@ -201,7 +202,7 @@ export const RobotConnected = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Open a Server-Sent Events (SSE) connection to receive live ping updates.
|
// Open a Server-Sent Events (SSE) connection to receive live ping updates.
|
||||||
// We're expecting a stream of data like that looks like this: `data = False` or `data = True`
|
// We're expecting a stream of data like that looks like this: `data = False` or `data = True`
|
||||||
const eventSource = new EventSource("http://localhost:8000/robot/ping_stream");
|
const eventSource = new EventSource(`${API_BASE_URL}/robot/ping_stream`);
|
||||||
eventSource.onmessage = (event) => {
|
eventSource.onmessage = (event) => {
|
||||||
|
|
||||||
// Expecting messages in JSON format: `true` or `false`
|
// Expecting messages in JSON format: `true` or `false`
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import formatDuration from "../../../utils/formatDuration.ts";
|
|||||||
import {create} from "zustand";
|
import {create} from "zustand";
|
||||||
import Dialog from "../../../components/Dialog.tsx";
|
import Dialog from "../../../components/Dialog.tsx";
|
||||||
import delayedResolve from "../../../utils/delayedResolve.ts";
|
import delayedResolve from "../../../utils/delayedResolve.ts";
|
||||||
|
import { API_BASE_URL } from "../../../config/api.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local Zustand store for logging UI preferences.
|
* Local Zustand store for logging UI preferences.
|
||||||
@@ -110,7 +111,7 @@ function DownloadScreen({filenames, refresh}: {filenames: string[] | null, refre
|
|||||||
|
|
||||||
return <ol className={`${styles.downloadList} margin-0 padding-h-lg scroll-y`}>
|
return <ol className={`${styles.downloadList} margin-0 padding-h-lg scroll-y`}>
|
||||||
{filenames!.map((filename) => (
|
{filenames!.map((filename) => (
|
||||||
<li><a key={filename} href={`http://localhost:8000/api/logs/files/${filename}`} download={filename}>{filename}</a></li>
|
<li><a key={filename} href={`${API_BASE_URL}/api/logs/files/${filename}`} download={filename}>{filename}</a></li>
|
||||||
))}
|
))}
|
||||||
</ol>;
|
</ol>;
|
||||||
})();
|
})();
|
||||||
@@ -130,7 +131,7 @@ function DownloadButton() {
|
|||||||
const [filenames, setFilenames] = useState<string[] | null>(null);
|
const [filenames, setFilenames] = useState<string[] | null>(null);
|
||||||
|
|
||||||
async function getFiles(): Promise<string[]> {
|
async function getFiles(): Promise<string[]> {
|
||||||
const response = await fetch("http://localhost:8000/api/logs/files");
|
const response = await fetch(`${API_BASE_URL}/api/logs/files`);
|
||||||
const files = await response.json();
|
const files = await response.json();
|
||||||
files.sort();
|
files.sort();
|
||||||
return files;
|
return files;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// University within the Software Project course.
|
// University within the Software Project course.
|
||||||
// © Copyright Utrecht University (Department of Information and Computing Sciences)
|
// © Copyright Utrecht University (Department of Information and Computing Sciences)
|
||||||
import { useState, useEffect, useRef } from 'react'
|
import { useState, useEffect, useRef } from 'react'
|
||||||
|
import { API_BASE_URL } from '../../config/api.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a live robot interaction panel with user input, conversation history,
|
* Displays a live robot interaction panel with user input, conversation history,
|
||||||
@@ -34,7 +35,7 @@ export default function Robot() {
|
|||||||
*/
|
*/
|
||||||
const sendMessage = async () => {
|
const sendMessage = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch("http://localhost:8000/message", {
|
const response = await fetch(`${API_BASE_URL}/message`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@@ -60,7 +61,7 @@ export default function Robot() {
|
|||||||
* The connection resets whenever `conversationIndex` changes.
|
* The connection resets whenever `conversationIndex` changes.
|
||||||
*/
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const eventSource = new EventSource("http://localhost:8000/sse");
|
const eventSource = new EventSource(`${API_BASE_URL}/sse`);
|
||||||
|
|
||||||
eventSource.onmessage = (event) => {
|
eventSource.onmessage = (event) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import orderPhaseNodeArray from "../../utils/orderPhaseNodes";
|
|||||||
import useFlowStore from './visualProgrammingUI/VisProgStores';
|
import useFlowStore from './visualProgrammingUI/VisProgStores';
|
||||||
import { NodeReduces } from './visualProgrammingUI/NodeRegistry';
|
import { NodeReduces } from './visualProgrammingUI/NodeRegistry';
|
||||||
import type { PhaseNode } from "./visualProgrammingUI/nodes/PhaseNode";
|
import type { PhaseNode } from "./visualProgrammingUI/nodes/PhaseNode";
|
||||||
|
import { API_BASE_URL } from "../../config/api.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reduces the graph into its phases' information and recursively calls their reducing function
|
* Reduces the graph into its phases' information and recursively calls their reducing function
|
||||||
@@ -28,7 +29,7 @@ export function runProgram() {
|
|||||||
const program = {phases}
|
const program = {phases}
|
||||||
console.log(JSON.stringify(program, null, 2));
|
console.log(JSON.stringify(program, null, 2));
|
||||||
fetch(
|
fetch(
|
||||||
"http://localhost:8000/program",
|
`${API_BASE_URL}/program`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {"Content-Type": "application/json"},
|
headers: {"Content-Type": "application/json"},
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{/*
|
/*
|
||||||
This program has been developed by students from the bachelor Computer Science at Utrecht
|
This program has been developed by students from the bachelor Computer Science at Utrecht
|
||||||
University within the Software Project course.
|
University within the Software Project course.
|
||||||
© Copyright Utrecht University (Department of Information and Computing Sciences)
|
© Copyright Utrecht University (Department of Information and Computing Sciences)
|
||||||
*/}
|
*/
|
||||||
.operator-switch {
|
.operator-switch {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
11
src/vite-env.d.ts
vendored
Normal file
11
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
readonly VITE_API_BASE_URL?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const __VITE_API_BASE_URL__: string | undefined;
|
||||||
@@ -6,6 +6,7 @@ import "@testing-library/jest-dom";
|
|||||||
import {type LogRecord, useLogs} from "../../../src/components/Logging/useLogs.ts";
|
import {type LogRecord, useLogs} from "../../../src/components/Logging/useLogs.ts";
|
||||||
import {type cell, useCell} from "../../../src/utils/cellStore.ts";
|
import {type cell, useCell} from "../../../src/utils/cellStore.ts";
|
||||||
import { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
|
import { API_BASE_URL } from "../../../src/config/api.ts";
|
||||||
|
|
||||||
jest.mock("../../../src/utils/priorityFiltering.ts", () => ({
|
jest.mock("../../../src/utils/priorityFiltering.ts", () => ({
|
||||||
applyPriorityPredicates: jest.fn((_log, preds: any[]) =>
|
applyPriorityPredicates: jest.fn((_log, preds: any[]) =>
|
||||||
@@ -83,7 +84,7 @@ describe("useLogs (unit)", () => {
|
|||||||
);
|
);
|
||||||
const es = (globalThis as any).__es as MockEventSource;
|
const es = (globalThis as any).__es as MockEventSource;
|
||||||
expect(es).toBeTruthy();
|
expect(es).toBeTruthy();
|
||||||
expect(es.url).toBe("http://localhost:8000/logs/stream");
|
expect(es.url).toBe(`${API_BASE_URL}/logs/stream`);
|
||||||
|
|
||||||
unmount();
|
unmount();
|
||||||
expect(es.close).toHaveBeenCalledTimes(1);
|
expect(es.close).toHaveBeenCalledTimes(1);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
useExperimentLogger,
|
useExperimentLogger,
|
||||||
useStatusLogger
|
useStatusLogger
|
||||||
} from '../../../src/pages/MonitoringPage/MonitoringPageAPI';
|
} from '../../../src/pages/MonitoringPage/MonitoringPageAPI';
|
||||||
|
import { API_BASE_URL } from '../../../src/config/api.ts';
|
||||||
|
|
||||||
// --- MOCK EVENT SOURCE SETUP ---
|
// --- MOCK EVENT SOURCE SETUP ---
|
||||||
// This mocks the browser's EventSource so we can manually 'push' messages to our hooks
|
// This mocks the browser's EventSource so we can manually 'push' messages to our hooks
|
||||||
@@ -72,7 +73,7 @@ describe('MonitoringPageAPI', () => {
|
|||||||
await sendAPICall('test_type', 'test_ctx');
|
await sendAPICall('test_type', 'test_ctx');
|
||||||
|
|
||||||
expect(globalThis.fetch).toHaveBeenCalledWith(
|
expect(globalThis.fetch).toHaveBeenCalledWith(
|
||||||
'http://localhost:8000/button_pressed',
|
`${API_BASE_URL}/button_pressed`,
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// © Copyright Utrecht University (Department of Information and Computing Sciences)
|
// © Copyright Utrecht University (Department of Information and Computing Sciences)
|
||||||
import { render, screen, act, cleanup, fireEvent } from '@testing-library/react';
|
import { render, screen, act, cleanup, fireEvent } from '@testing-library/react';
|
||||||
import Robot from '../../../src/pages/Robot/Robot';
|
import Robot from '../../../src/pages/Robot/Robot';
|
||||||
|
import { API_BASE_URL } from '../../../src/config/api.ts';
|
||||||
|
|
||||||
// Mock EventSource
|
// Mock EventSource
|
||||||
const mockInstances: MockEventSource[] = [];
|
const mockInstances: MockEventSource[] = [];
|
||||||
@@ -64,7 +65,7 @@ describe('Robot', () => {
|
|||||||
await act(async () => fireEvent.click(button));
|
await act(async () => fireEvent.click(button));
|
||||||
|
|
||||||
expect(globalThis.fetch).toHaveBeenCalledWith(
|
expect(globalThis.fetch).toHaveBeenCalledWith(
|
||||||
'http://localhost:8000/message',
|
`${API_BASE_URL}/message`,
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@@ -83,7 +84,7 @@ describe('Robot', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(globalThis.fetch).toHaveBeenCalledWith(
|
expect(globalThis.fetch).toHaveBeenCalledWith(
|
||||||
'http://localhost:8000/message',
|
`${API_BASE_URL}/message`,
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import react from '@vitejs/plugin-react'
|
|||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
|
define: {
|
||||||
|
__VITE_API_BASE_URL__: "import.meta.env.VITE_API_BASE_URL",
|
||||||
|
},
|
||||||
css: {
|
css: {
|
||||||
modules: {
|
modules: {
|
||||||
localsConvention: "camelCase",
|
localsConvention: "camelCase",
|
||||||
|
|||||||
Reference in New Issue
Block a user