Compare commits
No commits in common. "538caba9f4d9f24d96f6c4995f6807460cbfff7d" and "0216515492318a2f1953a223f73ea1c0e329345c" have entirely different histories.
538caba9f4
...
0216515492
@ -359,8 +359,8 @@ WORKDIR /opt/ollama
|
||||
#ENV OLLAMA_VERSION=https://github.com/intel/ipex-llm/releases/download/v2.3.0-nightly/ollama-ipex-llm-2.3.0b20250415-ubuntu.tgz
|
||||
|
||||
# NOTE: NO longer at github.com/intel -- now at ipex-llm
|
||||
ENV OLLAMA_VERSION=https://github.com/ipex-llm/ipex-llm/releases/download/v2.2.0/ollama-ipex-llm-2.2.0-ubuntu.tgz
|
||||
#ENV OLLAMA_VERSION=https://github.com/ipex-llm/ipex-llm/releases/download/v2.3.0-nightly/ollama-ipex-llm-2.3.0b20250429-ubuntu.tgz
|
||||
#ENV OLLAMA_VERSION=https://github.com/ipex-llm/ipex-llm/releases/download/v2.2.0/ollama-ipex-llm-2.2.0-ubuntu.tgz
|
||||
ENV OLLAMA_VERSION=https://github.com/ipex-llm/ipex-llm/releases/download/v2.3.0-nightly/ollama-ipex-llm-2.3.0b20250429-ubuntu.tgz
|
||||
RUN wget -qO - ${OLLAMA_VERSION} | \
|
||||
tar --strip-components=1 -C . -xzv
|
||||
|
||||
|
@ -180,22 +180,22 @@ services:
|
||||
- ./cache/grafana:/var/lib/grafana
|
||||
|
||||
|
||||
# loki:
|
||||
# image: grafana/loki
|
||||
# container_name: loki
|
||||
# restart: "always"
|
||||
# # env_file:
|
||||
# # - .env.grafana
|
||||
# ports:
|
||||
# - 3211:3100 # Grafana
|
||||
# networks:
|
||||
# - internal
|
||||
# command:
|
||||
# - -config.file=/loki-config.yaml
|
||||
# volumes:
|
||||
# # - ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
# - ./loki-config.yaml:/loki-config.yaml
|
||||
# - ./cache/loki:/loki
|
||||
loki:
|
||||
image: grafana/loki
|
||||
container_name: loki
|
||||
restart: "always"
|
||||
# env_file:
|
||||
# - .env.grafana
|
||||
ports:
|
||||
- 3211:3100 # Grafana
|
||||
networks:
|
||||
- internal
|
||||
command:
|
||||
- -config.file=/loki-config.yaml
|
||||
volumes:
|
||||
# - ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- ./loki-config.yaml:/loki-config.yaml
|
||||
- ./cache/loki:/loki
|
||||
|
||||
|
||||
networks:
|
||||
|
@ -1,46 +1,8 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "/static/css/main.3c3ddc39.css",
|
||||
"main.js": "/static/js/main.b1c48cdf.js",
|
||||
"static/js/732.b7e64c48.chunk.js": "/static/js/732.b7e64c48.chunk.js",
|
||||
"static/js/467.ed8fe19b.chunk.js": "/static/js/467.ed8fe19b.chunk.js",
|
||||
"static/js/761.64498d2a.chunk.js": "/static/js/761.64498d2a.chunk.js",
|
||||
"static/js/358.7641aa7d.chunk.js": "/static/js/358.7641aa7d.chunk.js",
|
||||
"static/js/448.0ebff170.chunk.js": "/static/js/448.0ebff170.chunk.js",
|
||||
"static/js/461.dcde3ae6.chunk.js": "/static/js/461.dcde3ae6.chunk.js",
|
||||
"static/js/109.fea37f16.chunk.js": "/static/js/109.fea37f16.chunk.js",
|
||||
"static/js/203.e56567f8.chunk.js": "/static/js/203.e56567f8.chunk.js",
|
||||
"static/js/621.a72de2cb.chunk.js": "/static/js/621.a72de2cb.chunk.js",
|
||||
"static/js/320.f30ff8c0.chunk.js": "/static/js/320.f30ff8c0.chunk.js",
|
||||
"static/js/314.5b42713c.chunk.js": "/static/js/314.5b42713c.chunk.js",
|
||||
"static/js/972.19d4b287.chunk.js": "/static/js/972.19d4b287.chunk.js",
|
||||
"static/js/126.9837c9af.chunk.js": "/static/js/126.9837c9af.chunk.js",
|
||||
"static/js/147.660f25b1.chunk.js": "/static/js/147.660f25b1.chunk.js",
|
||||
"static/js/807.a9b3c2ae.chunk.js": "/static/js/807.a9b3c2ae.chunk.js",
|
||||
"static/js/859.88148fa8.chunk.js": "/static/js/859.88148fa8.chunk.js",
|
||||
"static/js/282.876bf6b3.chunk.js": "/static/js/282.876bf6b3.chunk.js",
|
||||
"static/js/255.28a7c83e.chunk.js": "/static/js/255.28a7c83e.chunk.js",
|
||||
"static/js/922.10b19c61.chunk.js": "/static/js/922.10b19c61.chunk.js",
|
||||
"static/js/370.721ed12b.chunk.js": "/static/js/370.721ed12b.chunk.js",
|
||||
"static/js/674.cb2cf2f8.chunk.js": "/static/js/674.cb2cf2f8.chunk.js",
|
||||
"static/js/622.95b51007.chunk.js": "/static/js/622.95b51007.chunk.js",
|
||||
"static/js/824.71eb7c8f.chunk.js": "/static/js/824.71eb7c8f.chunk.js",
|
||||
"static/js/415.d031fb39.chunk.js": "/static/js/415.d031fb39.chunk.js",
|
||||
"static/js/349.dfb2510e.chunk.js": "/static/js/349.dfb2510e.chunk.js",
|
||||
"static/js/626.6df36496.chunk.js": "/static/js/626.6df36496.chunk.js",
|
||||
"static/js/453.6fae039d.chunk.js": "/static/js/453.6fae039d.chunk.js",
|
||||
"static/js/372.1ba03aa4.chunk.js": "/static/js/372.1ba03aa4.chunk.js",
|
||||
"static/js/974.879246cb.chunk.js": "/static/js/974.879246cb.chunk.js",
|
||||
"static/js/914.0546aa7a.chunk.js": "/static/js/914.0546aa7a.chunk.js",
|
||||
"static/js/987.378e3c51.chunk.js": "/static/js/987.378e3c51.chunk.js",
|
||||
"static/js/318.239ef60c.chunk.js": "/static/js/318.239ef60c.chunk.js",
|
||||
"static/js/929.7d5d6402.chunk.js": "/static/js/929.7d5d6402.chunk.js",
|
||||
"static/js/854.2162dcf9.chunk.js": "/static/js/854.2162dcf9.chunk.js",
|
||||
"static/js/502.89ac9055.chunk.js": "/static/js/502.89ac9055.chunk.js",
|
||||
"static/js/646.3df37337.chunk.js": "/static/js/646.3df37337.chunk.js",
|
||||
"static/js/62.785a92ce.chunk.js": "/static/js/62.785a92ce.chunk.js",
|
||||
"static/js/35.b34bde96.chunk.js": "/static/js/35.b34bde96.chunk.js",
|
||||
"static/js/355.143eaed8.chunk.js": "/static/js/355.143eaed8.chunk.js",
|
||||
"main.css": "/static/css/main.8e56f513.css",
|
||||
"main.js": "/static/js/main.6f07f748.js",
|
||||
"static/js/453.ec6f47ad.chunk.js": "/static/js/453.ec6f47ad.chunk.js",
|
||||
"static/media/roboto-latin-700-normal.woff2": "/static/media/roboto-latin-700-normal.c4d6cab43bec89049809.woff2",
|
||||
"static/media/roboto-latin-500-normal.woff2": "/static/media/roboto-latin-500-normal.599f66a60bdf974e578e.woff2",
|
||||
"static/media/roboto-latin-300-normal.woff2": "/static/media/roboto-latin-300-normal.db56943a88e4852343ae.woff2",
|
||||
@ -114,44 +76,12 @@
|
||||
"static/media/roboto-greek-ext-500-normal.woff": "/static/media/roboto-greek-ext-500-normal.1964239c2800b6bd7e39.woff",
|
||||
"static/media/roboto-greek-ext-300-normal.woff": "/static/media/roboto-greek-ext-300-normal.60729cafbded24073dfb.woff",
|
||||
"index.html": "/index.html",
|
||||
"main.3c3ddc39.css.map": "/static/css/main.3c3ddc39.css.map",
|
||||
"main.b1c48cdf.js.map": "/static/js/main.b1c48cdf.js.map",
|
||||
"732.b7e64c48.chunk.js.map": "/static/js/732.b7e64c48.chunk.js.map",
|
||||
"467.ed8fe19b.chunk.js.map": "/static/js/467.ed8fe19b.chunk.js.map",
|
||||
"761.64498d2a.chunk.js.map": "/static/js/761.64498d2a.chunk.js.map",
|
||||
"358.7641aa7d.chunk.js.map": "/static/js/358.7641aa7d.chunk.js.map",
|
||||
"448.0ebff170.chunk.js.map": "/static/js/448.0ebff170.chunk.js.map",
|
||||
"461.dcde3ae6.chunk.js.map": "/static/js/461.dcde3ae6.chunk.js.map",
|
||||
"109.fea37f16.chunk.js.map": "/static/js/109.fea37f16.chunk.js.map",
|
||||
"203.e56567f8.chunk.js.map": "/static/js/203.e56567f8.chunk.js.map",
|
||||
"621.a72de2cb.chunk.js.map": "/static/js/621.a72de2cb.chunk.js.map",
|
||||
"320.f30ff8c0.chunk.js.map": "/static/js/320.f30ff8c0.chunk.js.map",
|
||||
"314.5b42713c.chunk.js.map": "/static/js/314.5b42713c.chunk.js.map",
|
||||
"972.19d4b287.chunk.js.map": "/static/js/972.19d4b287.chunk.js.map",
|
||||
"126.9837c9af.chunk.js.map": "/static/js/126.9837c9af.chunk.js.map",
|
||||
"147.660f25b1.chunk.js.map": "/static/js/147.660f25b1.chunk.js.map",
|
||||
"807.a9b3c2ae.chunk.js.map": "/static/js/807.a9b3c2ae.chunk.js.map",
|
||||
"859.88148fa8.chunk.js.map": "/static/js/859.88148fa8.chunk.js.map",
|
||||
"282.876bf6b3.chunk.js.map": "/static/js/282.876bf6b3.chunk.js.map",
|
||||
"255.28a7c83e.chunk.js.map": "/static/js/255.28a7c83e.chunk.js.map",
|
||||
"922.10b19c61.chunk.js.map": "/static/js/922.10b19c61.chunk.js.map",
|
||||
"370.721ed12b.chunk.js.map": "/static/js/370.721ed12b.chunk.js.map",
|
||||
"674.cb2cf2f8.chunk.js.map": "/static/js/674.cb2cf2f8.chunk.js.map",
|
||||
"622.95b51007.chunk.js.map": "/static/js/622.95b51007.chunk.js.map",
|
||||
"824.71eb7c8f.chunk.js.map": "/static/js/824.71eb7c8f.chunk.js.map",
|
||||
"415.d031fb39.chunk.js.map": "/static/js/415.d031fb39.chunk.js.map",
|
||||
"349.dfb2510e.chunk.js.map": "/static/js/349.dfb2510e.chunk.js.map",
|
||||
"626.6df36496.chunk.js.map": "/static/js/626.6df36496.chunk.js.map",
|
||||
"453.6fae039d.chunk.js.map": "/static/js/453.6fae039d.chunk.js.map",
|
||||
"854.2162dcf9.chunk.js.map": "/static/js/854.2162dcf9.chunk.js.map",
|
||||
"502.89ac9055.chunk.js.map": "/static/js/502.89ac9055.chunk.js.map",
|
||||
"646.3df37337.chunk.js.map": "/static/js/646.3df37337.chunk.js.map",
|
||||
"62.785a92ce.chunk.js.map": "/static/js/62.785a92ce.chunk.js.map",
|
||||
"35.b34bde96.chunk.js.map": "/static/js/35.b34bde96.chunk.js.map",
|
||||
"355.143eaed8.chunk.js.map": "/static/js/355.143eaed8.chunk.js.map"
|
||||
"main.8e56f513.css.map": "/static/css/main.8e56f513.css.map",
|
||||
"main.6f07f748.js.map": "/static/js/main.6f07f748.js.map",
|
||||
"453.ec6f47ad.chunk.js.map": "/static/js/453.ec6f47ad.chunk.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.3c3ddc39.css",
|
||||
"static/js/main.b1c48cdf.js"
|
||||
"static/css/main.8e56f513.css",
|
||||
"static/js/main.6f07f748.js"
|
||||
]
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
Backstory is developed using:
|
||||
|
||||
## Frontend
|
||||
|
||||
* React
|
||||
* MUI
|
||||
* Plotly.js
|
||||
* MuiMarkdown
|
||||
* Mermaid
|
||||
|
||||
## Backend
|
||||
|
||||
* Python
|
||||
* FastAPI
|
||||
* HuggingFace Transformers
|
||||
* Ollama
|
||||
* Backstory Agent Framework
|
||||
* Prometheus
|
||||
* Grafana
|
||||
* ze-monitor
|
||||
* Jupyter Notebook
|
@ -1,11 +1,15 @@
|
||||
## Backstory is three things
|
||||
The backstory about Backstory...
|
||||
|
||||
1. **An interactive Q&A** -- let potential employers ask questions about an individual's work history (aka "Backstory".) Based on the content the job seeker has provided to the RAG system, that can provide insights into that individual's resume and curriculum vitae that are often left out when people are trying to fit everything onto one page.
|
||||
## Backstory is two things
|
||||
|
||||
1. An interactive Q&A -- let potential employers ask questions about an individual's work history (aka "Backstory".) Based on the content the job seeker has provided to the RAG system, that can provide insights into that individual's resume and curriculum vitae that are often left out when people are trying to fit everything onto one page.
|
||||
|
||||
|
||||
2. **A resume builder** -- if you have a job position, and you think this person might be a candidate, paste your job description and have a resume produced based on their data. If it looks interesting, reach out to them. If not, hopefully you've gained some insight into what drives them.
|
||||
2. A resume builder -- if you have a job position, and you think this person might be a candidate, paste your job description and have a resume produced based on their data. If it looks interesting, reach out to them. If not, hopefully you've gained some insight into what drives them.
|
||||
|
||||
3. **A curated expert about you** -- as a potential job seeker, you can self host this environment and generate resumes for yourself.
|
||||
-or-
|
||||
|
||||
2. A curated expert about you -- as a potential job seeker, you can self host this environment and generate resumes for yourself.
|
||||
|
||||
While this project was generally built for self-hosting with open source models, you can use any of the frontier models. The API adapters in this project can be configured to use infrastructure hosted from Anthropic, Google, Grok, and OpenAI (alphabetical.) For information, see [https://github.com/jketreno/backstory/README.md](https://github.com/jketreno/backstory/README.md#Frontier_Models).
|
||||
|
||||
@ -25,4 +29,4 @@ A. I could; but I don't want to store your data. I also don't want to have to be
|
||||
|
||||
Q. <ChatQuery prompt="Why can't I just ask Backstory these questions?" tunables={{ "enable_tools": false }} />
|
||||
|
||||
A. Try it. See what you find out :)
|
||||
A. Try it. See what you find out :)
|
@ -1,100 +0,0 @@
|
||||
The system follows a carefully designed pipeline with isolated stages to prevent fabrication:
|
||||
|
||||
## System Architecture Overview
|
||||
|
||||
The system uses a pipeline of isolated analysis and generation steps:
|
||||
|
||||
1. **Stage 1: Isolated Analysis** (three sub-stages)
|
||||
- **1A: Job Analysis** - Extracts requirements from job description only
|
||||
- **1B: Candidate Analysis** - Catalogs qualifications from resume/context only
|
||||
- **1C: Mapping Analysis** - Identifies legitimate matches between requirements and qualifications
|
||||
|
||||
2. **Stage 2: Resume Generation**
|
||||
- Uses mapping output to create a tailored resume with evidence-based content
|
||||
|
||||
3. **Stage 3: Verification**
|
||||
- Performs fact-checking to catch any remaining fabrications
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph "Stage 1: Isolated Analysis"
|
||||
subgraph "Stage 1A: Job Analysis"
|
||||
A1[Job Description Input] --> A2[Job Analysis LLM]
|
||||
A2 --> A3[Job Requirements JSON]
|
||||
end
|
||||
|
||||
subgraph "Stage 1B: Candidate Analysis"
|
||||
B1[Resume & Context Input] --> B2[Candidate Analysis LLM]
|
||||
B2 --> B3[Candidate Qualifications JSON]
|
||||
end
|
||||
|
||||
subgraph "Stage 1C: Mapping Analysis"
|
||||
C1[Job Requirements JSON] --> C2[Candidate Qualifications JSON]
|
||||
C2 --> C3[Mapping Analysis LLM]
|
||||
C3 --> C4[Skills Mapping JSON]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph "Stage 2: Resume Generation"
|
||||
D1[Skills Mapping JSON] --> D2[Original Resume Reference]
|
||||
D2 --> D3[Resume Generation LLM]
|
||||
D3 --> D4[Tailored Resume Draft]
|
||||
end
|
||||
|
||||
subgraph "Stage 3: Verification"
|
||||
E1[Skills Mapping JSON] --> E2[Original Materials]
|
||||
E2 --> E3[Tailored Resume Draft]
|
||||
E3 --> E4[Verification LLM]
|
||||
E4 --> E5{Verification Check}
|
||||
E5 -->|PASS| E6[Approved Resume]
|
||||
E5 -->|FAIL| E7[Correction Instructions]
|
||||
E7 --> D3
|
||||
end
|
||||
|
||||
A3 --> C1
|
||||
B3 --> C2
|
||||
C4 --> D1
|
||||
D4 --> E3
|
||||
|
||||
style A2 fill:#f9d77e,stroke:#333,stroke-width:2px
|
||||
style B2 fill:#f9d77e,stroke:#333,stroke-width:2px
|
||||
style C3 fill:#f9d77e,stroke:#333,stroke-width:2px
|
||||
style D3 fill:#f9d77e,stroke:#333,stroke-width:2px
|
||||
style E4 fill:#f9d77e,stroke:#333,stroke-width:2px
|
||||
style E5 fill:#a3e4d7,stroke:#333,stroke-width:2px
|
||||
style E6 fill:#aed6f1,stroke:#333,stroke-width:2px
|
||||
style E7 fill:#f5b7b1,stroke:#333,stroke-width:2px
|
||||
```
|
||||
|
||||
## Stage 1: Isolated Analysis (three separate sub-stages)
|
||||
|
||||
1. **Job Analysis**: Extracts requirements from just the job description
|
||||
2. **Candidate Analysis**: Catalogs qualifications from just the resume/context
|
||||
3. **Mapping Analysis**: Identifies legitimate matches between requirements and qualifications
|
||||
|
||||
## Stage 2: Resume Generation
|
||||
|
||||
Creates a tailored resume using only verified information from the mapping
|
||||
|
||||
## Stage 3: Verification
|
||||
|
||||
1. Performs fact-checking to catch any remaining fabrications
|
||||
2. Corrects issues if needed and re-verifies
|
||||
|
||||
### Key Anti-Fabrication Mechanisms
|
||||
|
||||
The system uses several techniques to prevent fabrication:
|
||||
|
||||
* **Isolation of Analysis Stages**: By analyzing the job and candidate separately, the system prevents the LLM from prematurely creating connections that might lead to fabrication.
|
||||
* **Evidence Requirements**: Each qualification included must have explicit evidence from the original materials.
|
||||
* **Conservative Transferability**: The system is instructed to be conservative when claiming skills are transferable.
|
||||
* **Verification Layer**: A dedicated verification step acts as a safety check to catch any remaining fabrications.
|
||||
* **Strict JSON Structures**: Using structured JSON formats ensures information flows properly between stages.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
* **Prompt Engineering**: Each stage has carefully designed prompts with clear instructions and output formats.
|
||||
* **Error Handling**: Comprehensive validation and error handling throughout the pipeline.
|
||||
* **Correction Loop**: If verification fails, the system attempts to correct issues and re-verify.
|
||||
* **Traceability**: Information in the final resume can be traced back to specific evidence in the original materials.
|
||||
|
@ -1 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Interactive chat with an enhanced LLM."/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Backstory</title><script defer="defer" src="/static/js/main.b1c48cdf.js"></script><link href="/static/css/main.3c3ddc39.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Interactive chat with an enhanced LLM."/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Backstory</title><script defer="defer" src="/static/js/main.6f07f748.js"></script><link href="/static/css/main.8e56f513.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
2
frontend/deployed/static/css/main.8e56f513.css
Normal file
2
frontend/deployed/static/css/main.8e56f513.css
Normal file
File diff suppressed because one or more lines are too long
1
frontend/deployed/static/css/main.8e56f513.css.map
Normal file
1
frontend/deployed/static/css/main.8e56f513.css.map
Normal file
File diff suppressed because one or more lines are too long
2
frontend/deployed/static/js/453.ec6f47ad.chunk.js
Normal file
2
frontend/deployed/static/js/453.ec6f47ad.chunk.js
Normal file
@ -0,0 +1,2 @@
|
||||
"use strict";(self.webpackChunkbackstory=self.webpackChunkbackstory||[]).push([[453],{453:(e,t,n)=>{n.r(t),n.d(t,{getCLS:()=>y,getFCP:()=>g,getFID:()=>F,getLCP:()=>P,getTTFB:()=>D});var i,r,a,o,u=function(e,t){return{name:e,value:void 0===t?-1:t,delta:0,entries:[],id:"v2-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)}},c=function(e,t){try{if(PerformanceObserver.supportedEntryTypes.includes(e)){if("first-input"===e&&!("PerformanceEventTiming"in self))return;var n=new PerformanceObserver((function(e){return e.getEntries().map(t)}));return n.observe({type:e,buffered:!0}),n}}catch(e){}},s=function(e,t){var n=function n(i){"pagehide"!==i.type&&"hidden"!==document.visibilityState||(e(i),t&&(removeEventListener("visibilitychange",n,!0),removeEventListener("pagehide",n,!0)))};addEventListener("visibilitychange",n,!0),addEventListener("pagehide",n,!0)},f=function(e){addEventListener("pageshow",(function(t){t.persisted&&e(t)}),!0)},m=function(e,t,n){var i;return function(r){t.value>=0&&(r||n)&&(t.delta=t.value-(i||0),(t.delta||void 0===i)&&(i=t.value,e(t)))}},v=-1,p=function(){return"hidden"===document.visibilityState?0:1/0},d=function(){s((function(e){var t=e.timeStamp;v=t}),!0)},l=function(){return v<0&&(v=p(),d(),f((function(){setTimeout((function(){v=p(),d()}),0)}))),{get firstHiddenTime(){return v}}},g=function(e,t){var n,i=l(),r=u("FCP"),a=function(e){"first-contentful-paint"===e.name&&(s&&s.disconnect(),e.startTime<i.firstHiddenTime&&(r.value=e.startTime,r.entries.push(e),n(!0)))},o=window.performance&&performance.getEntriesByName&&performance.getEntriesByName("first-contentful-paint")[0],s=o?null:c("paint",a);(o||s)&&(n=m(e,r,t),o&&a(o),f((function(i){r=u("FCP"),n=m(e,r,t),requestAnimationFrame((function(){requestAnimationFrame((function(){r.value=performance.now()-i.timeStamp,n(!0)}))}))})))},h=!1,T=-1,y=function(e,t){h||(g((function(e){T=e.value})),h=!0);var n,i=function(t){T>-1&&e(t)},r=u("CLS",0),a=0,o=[],v=function(e){if(!e.hadRecentInput){var t=o[0],i=o[o.length-1];a&&e.startTime-i.startTime<1e3&&e.startTime-t.startTime<5e3?(a+=e.value,o.push(e)):(a=e.value,o=[e]),a>r.value&&(r.value=a,r.entries=o,n())}},p=c("layout-shift",v);p&&(n=m(i,r,t),s((function(){p.takeRecords().map(v),n(!0)})),f((function(){a=0,T=-1,r=u("CLS",0),n=m(i,r,t)})))},E={passive:!0,capture:!0},w=new Date,L=function(e,t){i||(i=t,r=e,a=new Date,k(removeEventListener),S())},S=function(){if(r>=0&&r<a-w){var e={entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+r};o.forEach((function(t){t(e)})),o=[]}},b=function(e){if(e.cancelable){var t=(e.timeStamp>1e12?new Date:performance.now())-e.timeStamp;"pointerdown"==e.type?function(e,t){var n=function(){L(e,t),r()},i=function(){r()},r=function(){removeEventListener("pointerup",n,E),removeEventListener("pointercancel",i,E)};addEventListener("pointerup",n,E),addEventListener("pointercancel",i,E)}(t,e):L(t,e)}},k=function(e){["mousedown","keydown","touchstart","pointerdown"].forEach((function(t){return e(t,b,E)}))},F=function(e,t){var n,a=l(),v=u("FID"),p=function(e){e.startTime<a.firstHiddenTime&&(v.value=e.processingStart-e.startTime,v.entries.push(e),n(!0))},d=c("first-input",p);n=m(e,v,t),d&&s((function(){d.takeRecords().map(p),d.disconnect()}),!0),d&&f((function(){var a;v=u("FID"),n=m(e,v,t),o=[],r=-1,i=null,k(addEventListener),a=p,o.push(a),S()}))},C={},P=function(e,t){var n,i=l(),r=u("LCP"),a=function(e){var t=e.startTime;t<i.firstHiddenTime&&(r.value=t,r.entries.push(e),n())},o=c("largest-contentful-paint",a);if(o){n=m(e,r,t);var v=function(){C[r.id]||(o.takeRecords().map(a),o.disconnect(),C[r.id]=!0,n(!0))};["keydown","click"].forEach((function(e){addEventListener(e,v,{once:!0,capture:!0})})),s(v,!0),f((function(i){r=u("LCP"),n=m(e,r,t),requestAnimationFrame((function(){requestAnimationFrame((function(){r.value=performance.now()-i.timeStamp,C[r.id]=!0,n(!0)}))}))}))}},D=function(e){var t,n=u("TTFB");t=function(){try{var t=performance.getEntriesByType("navigation")[0]||function(){var e=performance.timing,t={entryType:"navigation",startTime:0};for(var n in e)"navigationStart"!==n&&"toJSON"!==n&&(t[n]=Math.max(e[n]-e.navigationStart,0));return t}();if(n.value=n.delta=t.responseStart,n.value<0||n.value>performance.now())return;n.entries=[t],e(n)}catch(e){}},"complete"===document.readyState?setTimeout(t,0):addEventListener("load",(function(){return setTimeout(t,0)}))}}}]);
|
||||
//# sourceMappingURL=453.ec6f47ad.chunk.js.map
|
1
frontend/deployed/static/js/453.ec6f47ad.chunk.js.map
Normal file
1
frontend/deployed/static/js/453.ec6f47ad.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
103
frontend/deployed/static/js/main.6f07f748.js
Normal file
103
frontend/deployed/static/js/main.6f07f748.js
Normal file
File diff suppressed because one or more lines are too long
149
frontend/deployed/static/js/main.6f07f748.js.LICENSE.txt
Normal file
149
frontend/deployed/static/js/main.6f07f748.js.LICENSE.txt
Normal file
@ -0,0 +1,149 @@
|
||||
/*!
|
||||
* Determine if an object is a Buffer
|
||||
*
|
||||
* @author Feross Aboukhadijeh <https://feross.org>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
* The buffer module from node.js, for the browser.
|
||||
*
|
||||
* @author Feross Aboukhadijeh <https://feross.org>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
* pad-left <https://github.com/jonschlinkert/pad-left>
|
||||
*
|
||||
* Copyright (c) 2014-2015, Jon Schlinkert.
|
||||
* Licensed under the MIT license.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* repeat-string <https://github.com/jonschlinkert/repeat-string>
|
||||
*
|
||||
* Copyright (c) 2014-2015, Jon Schlinkert.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
/*! Bundled license information:
|
||||
|
||||
native-promise-only/lib/npo.src.js:
|
||||
(*! Native Promise Only
|
||||
v0.8.1 (c) Kyle Simpson
|
||||
MIT License: http://getify.mit-license.org
|
||||
*)
|
||||
|
||||
polybooljs/index.js:
|
||||
(*
|
||||
* @copyright 2016 Sean Connelly (@voidqk), http://syntheti.cc
|
||||
* @license MIT
|
||||
* @preserve Project Home: https://github.com/voidqk/polybooljs
|
||||
*)
|
||||
|
||||
ieee754/index.js:
|
||||
(*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> *)
|
||||
|
||||
buffer/index.js:
|
||||
(*!
|
||||
* The buffer module from node.js, for the browser.
|
||||
*
|
||||
* @author Feross Aboukhadijeh <https://feross.org>
|
||||
* @license MIT
|
||||
*)
|
||||
|
||||
safe-buffer/index.js:
|
||||
(*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> *)
|
||||
|
||||
assert/build/internal/util/comparisons.js:
|
||||
(*!
|
||||
* The buffer module from node.js, for the browser.
|
||||
*
|
||||
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
|
||||
* @license MIT
|
||||
*)
|
||||
|
||||
object-assign/index.js:
|
||||
(*
|
||||
object-assign
|
||||
(c) Sindre Sorhus
|
||||
@license MIT
|
||||
*)
|
||||
|
||||
maplibre-gl/dist/maplibre-gl.js:
|
||||
(**
|
||||
* MapLibre GL JS
|
||||
* @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v4.7.1/LICENSE.txt
|
||||
*)
|
||||
*/
|
||||
|
||||
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-dom-client.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-dom.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-is.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-jsx-runtime.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* scheduler.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v16.13.1
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
1
frontend/deployed/static/js/main.6f07f748.js.map
Normal file
1
frontend/deployed/static/js/main.6f07f748.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -1,80 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import { Scrollable } from './Scrollable';
|
||||
import { BackstoryPageProps } from './BackstoryTab';
|
||||
import { Document } from './Document';
|
||||
|
||||
const AboutPage = (props: BackstoryPageProps) => {
|
||||
const { sessionId, submitQuery, setSnack, route, setRoute } = props;
|
||||
const [ page, setPage ] = useState<string>("");
|
||||
const [ subRoute, setSubRoute] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
console.log(`AboutPage: ${page} - route - ${route} - subRoute: ${subRoute}`);
|
||||
}, [page, route, subRoute]);
|
||||
|
||||
useEffect(() => {
|
||||
if (route === undefined) { return; }
|
||||
const parts = route.split("/");
|
||||
if (parts.length === 0) { return; }
|
||||
setPage(parts[0]);
|
||||
if (parts.length > 1) {
|
||||
parts.shift();
|
||||
setSubRoute(parts.join("/"));
|
||||
}
|
||||
}, [route, setPage, setSubRoute]);
|
||||
|
||||
const onDocumentExpand = (document: string, open: boolean) => {
|
||||
console.log("Document expanded:", document, open);
|
||||
if (open) {
|
||||
setPage(document);
|
||||
if (setRoute) setRoute(document);
|
||||
} else {
|
||||
setPage("");
|
||||
if (setRoute) setRoute("");
|
||||
}
|
||||
}
|
||||
|
||||
return <Scrollable
|
||||
autoscroll={false}
|
||||
sx={{
|
||||
maxWidth: "1024px",
|
||||
height: "calc(100vh - 72px)",
|
||||
flexDirection: "column",
|
||||
margin: "0 auto",
|
||||
p: 1,
|
||||
}}
|
||||
>
|
||||
<Document {...{
|
||||
title: "About",
|
||||
filepath: "/docs/about.md",
|
||||
onExpand: (open: boolean) => { onDocumentExpand('about', open); },
|
||||
expanded: page === 'about',
|
||||
sessionId,
|
||||
submitQuery: submitQuery,
|
||||
setSnack,
|
||||
}} />
|
||||
<Document {...{
|
||||
title: "Resume Generation Architecture",
|
||||
filepath: "/docs/resume-generation.md",
|
||||
onExpand: (open: boolean) => { onDocumentExpand('resume-generation', open); },
|
||||
expanded: page === 'resume-generation',
|
||||
sessionId,
|
||||
submitQuery: submitQuery,
|
||||
setSnack,
|
||||
}} />
|
||||
<Document {...{
|
||||
title: "Application Architecture",
|
||||
filepath: "/docs/about-app.md",
|
||||
onExpand: (open: boolean) => { onDocumentExpand('about-app', open); },
|
||||
expanded: page === 'about-app',
|
||||
sessionId,
|
||||
submitQuery: submitQuery,
|
||||
setSnack,
|
||||
}} />
|
||||
</Scrollable>;
|
||||
};
|
||||
|
||||
export {
|
||||
AboutPage
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
||||
import React, { ReactElement, JSXElementConstructor, useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import Card from '@mui/material/Card';
|
||||
import { styled } from '@mui/material/styles';
|
||||
@ -14,22 +14,20 @@ import IconButton from '@mui/material/IconButton';
|
||||
import Box from '@mui/material/Box';
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { SxProps } from '@mui/material';
|
||||
|
||||
|
||||
import { ResumeBuilder } from './ResumeBuilder';
|
||||
import { MessageList } from './Message';
|
||||
import { Snack, SeverityType } from './Snack';
|
||||
import { ConversationHandle } from './Conversation';
|
||||
import { QueryOptions } from './ChatQuery';
|
||||
import { VectorVisualizer } from './VectorVisualizer';
|
||||
import { Controls } from './Controls';
|
||||
import { Conversation, ConversationHandle } from './Conversation';
|
||||
import { ChatQuery, QueryOptions } from './ChatQuery';
|
||||
import { Scrollable } from './Scrollable';
|
||||
import { BackstoryPage, BackstoryTabProps } from './BackstoryTab';
|
||||
|
||||
import { connectionBase } from './Global';
|
||||
|
||||
import { HomePage } from './HomePage';
|
||||
import { ResumeBuilderPage } from './ResumeBuilderPage';
|
||||
import { VectorVisualizerPage } from './VectorVisualizer';
|
||||
import { AboutPage } from './AboutPage';
|
||||
import { ControlsPage } from './ControlsPage';
|
||||
|
||||
import { BackstoryTab } from './BackstoryTab';
|
||||
import { Document } from './Document';
|
||||
|
||||
import './App.css';
|
||||
import './Conversation.css';
|
||||
@ -39,7 +37,31 @@ import '@fontsource/roboto/400.css';
|
||||
import '@fontsource/roboto/500.css';
|
||||
import '@fontsource/roboto/700.css';
|
||||
|
||||
import MuiMarkdown from 'mui-markdown';
|
||||
|
||||
const getConnectionBase = (loc: any): string => {
|
||||
console.log(`getConnectionBase(${loc})`)
|
||||
if (!loc.host.match(/.*battle-linux.*/)) {
|
||||
return loc.protocol + "//" + loc.host;
|
||||
} else {
|
||||
return loc.protocol + "//battle-linux.ketrenos.com:8912";
|
||||
}
|
||||
}
|
||||
|
||||
const connectionBase = getConnectionBase(window.location);
|
||||
|
||||
interface TabProps {
|
||||
label?: string,
|
||||
path: string,
|
||||
route?: string,
|
||||
children?: any,
|
||||
tabProps?: {
|
||||
label?: string,
|
||||
sx?: SxProps,
|
||||
icon?: string | ReactElement<unknown, string | JSXElementConstructor<any>> | undefined,
|
||||
iconPosition?: "bottom" | "top" | "start" | "end" | undefined
|
||||
}
|
||||
};
|
||||
|
||||
const isValidUUIDv4 = (str: string): boolean => {
|
||||
const pattern = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89ab][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/i;
|
||||
@ -54,8 +76,9 @@ const App = () => {
|
||||
const isDesktop = useMediaQuery('(min-width:650px)');
|
||||
const prevIsDesktopRef = useRef<boolean>(isDesktop);
|
||||
const chatRef = useRef<ConversationHandle>(null);
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||||
const snackRef = useRef<any>(null);
|
||||
const [subRoute, setSubRoute] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
if (prevIsDesktopRef.current === isDesktop)
|
||||
@ -78,13 +101,51 @@ const App = () => {
|
||||
setActiveTab(0);
|
||||
};
|
||||
|
||||
const tabs: BackstoryTabProps[] = useMemo(() => {
|
||||
const homeTab: BackstoryTabProps = {
|
||||
const onDocumentExpand = (document: string) => {
|
||||
console.log("Document expanded:", document);
|
||||
}
|
||||
|
||||
const tabs: TabProps[] = useMemo(() => {
|
||||
console.log(document);
|
||||
const backstoryPreamble: MessageList = [
|
||||
{
|
||||
role: 'content',
|
||||
title: 'Welcome to Backstory',
|
||||
disableCopy: true,
|
||||
content: `
|
||||
Backstory is a RAG enabled expert system with access to real-time data running self-hosted
|
||||
(no cloud) versions of industry leading Large and Small Language Models (LLM/SLMs).
|
||||
It was written by James Ketrenos in order to provide answers to
|
||||
questions potential employers may have about his work history.
|
||||
|
||||
What would you like to know about James?
|
||||
`
|
||||
}
|
||||
];
|
||||
|
||||
const backstoryQuestions = [
|
||||
<Box sx={{ display: "flex", flexDirection: isMobile ? "column" : "row" }}>
|
||||
<ChatQuery prompt="What is James Ketrenos' work history?" tunables={{ enable_tools: false }} submitQuery={handleSubmitChatQuery} />
|
||||
<ChatQuery prompt="What programming languages has James used?" tunables={{ enable_tools: false }} submitQuery={handleSubmitChatQuery} />
|
||||
<ChatQuery prompt="What are James' professional strengths?" tunables={{ enable_tools: false }} submitQuery={handleSubmitChatQuery} />
|
||||
<ChatQuery prompt="What are today's headlines on CNBC.com?" tunables={{ enable_tools: true, enable_rag: false, enable_context: false }} submitQuery={handleSubmitChatQuery} />
|
||||
</Box>,
|
||||
<Box sx={{ p: 1 }}>
|
||||
<MuiMarkdown>
|
||||
As with all LLM interactions, the results may not be 100% accurate. If you have questions about my career,
|
||||
I'd love to hear from you. You can send me an email at **james_backstory@ketrenos.com**.
|
||||
</MuiMarkdown>
|
||||
</Box>
|
||||
];
|
||||
|
||||
const tabSx = { flexGrow: 1, fontSize: '1rem' };
|
||||
|
||||
return [{
|
||||
label: "",
|
||||
path: "",
|
||||
tabProps: {
|
||||
label: "Backstory",
|
||||
sx: { flexGrow: 1, fontSize: '1rem' },
|
||||
sx: tabSx,
|
||||
icon:
|
||||
<Avatar sx={{
|
||||
width: 24,
|
||||
@ -95,73 +156,121 @@ const App = () => {
|
||||
src="/logo192.png" />,
|
||||
iconPosition: "start"
|
||||
},
|
||||
children: <HomePage ref={chatRef} {...{ sessionId, setSnack, submitQuery: handleSubmitChatQuery, route: subRoute, setRoute: setSubRoute }} />
|
||||
};
|
||||
|
||||
const resumeBuilderTab: BackstoryTabProps = {
|
||||
label: "Resume Builder",
|
||||
path: "resume-builder",
|
||||
children: <ResumeBuilderPage {...{ sessionId, setSnack, submitQuery: handleSubmitChatQuery, route: subRoute, setRoute: setSubRoute }} />
|
||||
};
|
||||
|
||||
const contextVisualizerTab: BackstoryTabProps = {
|
||||
label: "Context Visualizer",
|
||||
path: "context-visualizer",
|
||||
children: <VectorVisualizerPage sx={{ p: 1 }} {...{ sessionId, setSnack, submitQuery: handleSubmitChatQuery, route: subRoute, setRoute: setSubRoute }} />
|
||||
};
|
||||
|
||||
const aboutTab = {
|
||||
label: "About",
|
||||
path: "about",
|
||||
children: <AboutPage {...{ sessionId, setSnack, submitQuery: handleSubmitChatQuery, route: subRoute, setRoute: setSubRoute }} />
|
||||
};
|
||||
|
||||
const settingsTab: BackstoryTabProps = {
|
||||
path: "settings",
|
||||
tabProps: {
|
||||
sx: { flexShrink: 1, flexGrow: 0, fontSize: '1rem' },
|
||||
icon: <SettingsIcon />
|
||||
},
|
||||
children: (
|
||||
<Scrollable
|
||||
autoscroll={false}
|
||||
<Conversation
|
||||
sx={{
|
||||
maxWidth: "1024px",
|
||||
height: "calc(100vh - 72px)",
|
||||
flexDirection: "column",
|
||||
margin: "0 auto",
|
||||
p: 1,
|
||||
}}
|
||||
>
|
||||
{sessionId !== undefined &&
|
||||
<ControlsPage {...{ sessionId, setSnack, submitQuery: handleSubmitChatQuery, route: subRoute, setRoute: setSubRoute }} />
|
||||
}
|
||||
</Scrollable>
|
||||
ref={chatRef}
|
||||
{...{
|
||||
type: "chat",
|
||||
prompt: "What would you like to know about James?",
|
||||
resetLabel: "chat",
|
||||
sessionId,
|
||||
connectionBase,
|
||||
setSnack,
|
||||
preamble: backstoryPreamble,
|
||||
defaultPrompts: backstoryQuestions
|
||||
}}
|
||||
/>
|
||||
)
|
||||
};
|
||||
|
||||
return [
|
||||
homeTab,
|
||||
resumeBuilderTab,
|
||||
contextVisualizerTab,
|
||||
aboutTab,
|
||||
settingsTab,
|
||||
];
|
||||
}, [sessionId, setSnack, subRoute]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (sessionId === undefined || activeTab > tabs.length - 1) { return; }
|
||||
console.log(`route - '${tabs[activeTab].path}', subRoute - '${subRoute}'`);
|
||||
|
||||
let path = tabs[activeTab].path ? `/${tabs[activeTab].path}` : '';
|
||||
if (subRoute) {
|
||||
path += `/${subRoute}`;
|
||||
}
|
||||
path += `/${sessionId}`;
|
||||
console.log('pushState: ', path);
|
||||
// window.history.pushState({}, '', path);
|
||||
}, [activeTab, sessionId, subRoute, tabs]);
|
||||
}, {
|
||||
label: "Resume Builder",
|
||||
path: "resume-builder",
|
||||
children: (
|
||||
<ResumeBuilder sx={{
|
||||
margin: "0 auto",
|
||||
height: "calc(100vh - 72px)",
|
||||
overflow: "auto",
|
||||
backgroundColor: "#F5F5F5",
|
||||
display: "flex",
|
||||
flexGrow: 1
|
||||
}} {...{ setSnack, connectionBase, sessionId }}
|
||||
/>
|
||||
)
|
||||
}, {
|
||||
label: "Context Visualizer",
|
||||
path: "context-visualizer",
|
||||
children:
|
||||
<Scrollable
|
||||
sx={{
|
||||
maxWidth: "1024px",
|
||||
height: "calc(100vh - 72px)",
|
||||
}}
|
||||
>
|
||||
<VectorVisualizer sx={{ p: 1 }} {...{ connectionBase, sessionId, setSnack }} />
|
||||
</Scrollable>
|
||||
}, {
|
||||
label: "About",
|
||||
path: "about",
|
||||
children: (
|
||||
<Scrollable
|
||||
autoscroll={false}
|
||||
sx={{
|
||||
maxWidth: "1024px",
|
||||
height: "calc(100vh - 72px)",
|
||||
flexDirection: "column",
|
||||
margin: "0 auto",
|
||||
p: 1,
|
||||
}}
|
||||
>
|
||||
<Document {...{
|
||||
title: "About",
|
||||
filepath: "/docs/about.md",
|
||||
onExpand: () => { onDocumentExpand('about'); },
|
||||
expanded: false,//openDocument === 'about',
|
||||
sessionId,
|
||||
connectionBase,
|
||||
submitQuery: handleSubmitChatQuery,
|
||||
setSnack,
|
||||
}} />
|
||||
<Document {...{
|
||||
title: "Resume Generation Architecture",
|
||||
filepath: "/docs/resume-generation.md",
|
||||
onExpand: () => { onDocumentExpand('resume-generation'); },
|
||||
expanded: false, //openDocument === 'about',
|
||||
sessionId,
|
||||
connectionBase,
|
||||
submitQuery: handleSubmitChatQuery,
|
||||
setSnack,
|
||||
}} />
|
||||
<Document {...{
|
||||
title: "Application Architecture",
|
||||
filepath: "/docs/about-app.md",
|
||||
onExpand: () => { onDocumentExpand('resume-generation'); },
|
||||
expanded: false, //openDocument === 'about-app',
|
||||
sessionId,
|
||||
connectionBase,
|
||||
submitQuery: handleSubmitChatQuery,
|
||||
setSnack,
|
||||
}} />
|
||||
</Scrollable>
|
||||
)
|
||||
}, {
|
||||
path: "settings",
|
||||
tabProps: {
|
||||
sx: { flexShrink: 1, flexGrow: 0, fontSize: '1rem' },
|
||||
icon: <SettingsIcon />
|
||||
},
|
||||
children: (
|
||||
<Scrollable
|
||||
autoscroll={false}
|
||||
sx={{
|
||||
maxWidth: "1024px",
|
||||
height: "calc(100vh - 72px)",
|
||||
flexDirection: "column",
|
||||
margin: "0 auto",
|
||||
p: 1,
|
||||
}}
|
||||
>
|
||||
{sessionId !== undefined &&
|
||||
<Controls {...{ sessionId, setSnack, connectionBase }} />
|
||||
}
|
||||
</Scrollable>
|
||||
)
|
||||
}];
|
||||
}, [sessionId, setSnack, isMobile]);
|
||||
|
||||
const fetchSession = useCallback((async (pathParts?: string[]) => {
|
||||
try {
|
||||
@ -191,7 +300,7 @@ const App = () => {
|
||||
setActiveTab(0);
|
||||
} else {
|
||||
window.history.replaceState({}, '', `/${pathParts.join('/')}/${new_session}`);
|
||||
// tabs[tabIndex].route = pathParts[2] || "";
|
||||
tabs[tabIndex].route = pathParts[2] || "";
|
||||
setActiveTab(tabIndex);
|
||||
}
|
||||
setSessionId(new_session);
|
||||
@ -221,7 +330,7 @@ const App = () => {
|
||||
console.log(`Invalid path "${currentPath}" -- redirecting to default`);
|
||||
tabIndex = 0;
|
||||
}
|
||||
// tabs[tabIndex].route = pathParts[2] || ""
|
||||
tabs[tabIndex].route = pathParts[2] || ""
|
||||
setSessionId(path_session);
|
||||
setActiveTab(tabIndex);
|
||||
}
|
||||
@ -388,7 +497,7 @@ const App = () => {
|
||||
</Box>
|
||||
{
|
||||
tabs.map((tab: any, i: number) =>
|
||||
<BackstoryPage key={i} active={i === activeTab} path={tab.path}>{tab.children}</BackstoryPage>
|
||||
<BackstoryTab key={i} active={i === activeTab}>{tab.children}</BackstoryTab>
|
||||
)
|
||||
}
|
||||
</Box>
|
||||
|
@ -1,54 +1,28 @@
|
||||
import React, { ReactElement, JSXElementConstructor } from 'react';
|
||||
import React from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import { SxProps, Theme } from '@mui/material';
|
||||
import { ChatSubmitQueryInterface } from './ChatQuery';
|
||||
import { SetSnackType } from './Snack';
|
||||
|
||||
interface BackstoryElementProps {
|
||||
sessionId: string | undefined,
|
||||
setSnack: SetSnackType,
|
||||
submitQuery: ChatSubmitQueryInterface,
|
||||
sx?: SxProps<Theme>,
|
||||
}
|
||||
|
||||
interface BackstoryPageProps extends BackstoryElementProps {
|
||||
route?: string,
|
||||
setRoute?: (route: string) => void,
|
||||
};
|
||||
|
||||
interface BackstoryTabProps {
|
||||
label?: string,
|
||||
path: string,
|
||||
children?: ReactElement<BackstoryPageProps>,
|
||||
active?: boolean,
|
||||
children?: React.ReactNode;
|
||||
sx?: SxProps<Theme>,
|
||||
className?: string,
|
||||
tabProps?: {
|
||||
label?: string,
|
||||
sx?: SxProps,
|
||||
icon?: string | ReactElement<unknown, string | JSXElementConstructor<any>> | undefined,
|
||||
iconPosition?: "bottom" | "top" | "start" | "end" | undefined
|
||||
}
|
||||
};
|
||||
active: boolean,
|
||||
}
|
||||
|
||||
function BackstoryPage(props: BackstoryTabProps) {
|
||||
const { className, active, children } = props;
|
||||
function BackstoryTab(props: BackstoryTabProps) {
|
||||
const { className, active, children, sx, ...other } = props;
|
||||
|
||||
return (
|
||||
<Box
|
||||
className={ className || "BackstoryTab"}
|
||||
sx={{ "display": active ? "flex" : "none", p: 0, m: 0, borders: "none" }}
|
||||
sx={{ "display": active ? "flex": "none", ...sx }}
|
||||
{...other}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export type {
|
||||
BackstoryPageProps,
|
||||
BackstoryTabProps,
|
||||
BackstoryElementProps,
|
||||
};
|
||||
|
||||
export {
|
||||
BackstoryPage
|
||||
BackstoryTab
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { Box } from '@mui/material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { SxProps, Theme } from '@mui/material';
|
||||
@ -20,11 +20,12 @@ interface ChatBubbleProps {
|
||||
title?: string;
|
||||
expanded?: boolean;
|
||||
expandable?: boolean;
|
||||
onExpand?: (open: boolean) => void;
|
||||
onExpand?: () => void;
|
||||
}
|
||||
|
||||
function ChatBubble(props: ChatBubbleProps) {
|
||||
const { role, children, sx, className, title, onExpand, expandable, expanded } = props;
|
||||
const { role, children, sx, className, title, onExpand, expandable }: ChatBubbleProps = props;
|
||||
const [expanded, setExpanded] = useState<boolean>((props.expanded === undefined) ? true : props.expanded);
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
@ -32,12 +33,12 @@ function ChatBubble(props: ChatBubbleProps) {
|
||||
const defaultStyle = {
|
||||
padding: theme.spacing(1, 2),
|
||||
fontSize: '0.875rem',
|
||||
alignSelf: 'flex-start',
|
||||
alignSelf: 'flex-start', // Left-aligned is used by default
|
||||
maxWidth: '100%',
|
||||
minWidth: '100%',
|
||||
height: 'fit-content',
|
||||
'& > *': {
|
||||
color: 'inherit',
|
||||
color: 'inherit', // Children inherit 'color' from parent
|
||||
overflow: 'hidden',
|
||||
m: 0,
|
||||
},
|
||||
@ -45,151 +46,130 @@ function ChatBubble(props: ChatBubbleProps) {
|
||||
mb: 0,
|
||||
m: 0,
|
||||
p: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const styles: any = {
|
||||
assistant: {
|
||||
'assistant': {
|
||||
...defaultStyle,
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
border: `1px solid ${theme.palette.secondary.main}`,
|
||||
borderRadius: `${defaultRadius} ${defaultRadius} ${defaultRadius} 0`,
|
||||
color: theme.palette.primary.contrastText,
|
||||
backgroundColor: theme.palette.primary.main, // Midnight Blue (#1A2536)
|
||||
border: `1px solid ${theme.palette.secondary.main}`, // Dusty Teal (#4A7A7D)
|
||||
borderRadius: `${defaultRadius} ${defaultRadius} ${defaultRadius} 0`, // Rounded, flat bottom-left for assistant
|
||||
color: theme.palette.primary.contrastText, // Warm Gray (#D3CDBF) for text
|
||||
},
|
||||
content: {
|
||||
'content': {
|
||||
...defaultStyle,
|
||||
backgroundColor: '#F5F2EA',
|
||||
border: `1px solid ${theme.palette.custom.highlight}`,
|
||||
backgroundColor: '#F5F2EA', // Light cream background for easy reading
|
||||
border: `1px solid ${theme.palette.custom.highlight}`, // Golden Ochre border
|
||||
borderRadius: 0,
|
||||
alignSelf: 'center',
|
||||
color: theme.palette.text.primary,
|
||||
padding: '8px 8px',
|
||||
marginBottom: '0px',
|
||||
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.05)',
|
||||
fontSize: '0.9rem',
|
||||
lineHeight: '1.3',
|
||||
fontFamily: theme.typography.fontFamily,
|
||||
alignSelf: 'center', // Centered in the chat
|
||||
color: theme.palette.text.primary, // Charcoal Black for maximum readability
|
||||
padding: '8px 8px', // More generous padding for better text framing
|
||||
marginBottom: '0px', // Space between content and conversation
|
||||
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.05)', // Subtle elevation
|
||||
fontSize: '0.9rem', // Slightly smaller than default
|
||||
lineHeight: '1.3', // More compact line height
|
||||
fontFamily: theme.typography.fontFamily, // Consistent font with your theme
|
||||
},
|
||||
error: {
|
||||
'error': {
|
||||
...defaultStyle,
|
||||
backgroundColor: '#F8E7E7',
|
||||
border: `1px solid #D83A3A`,
|
||||
backgroundColor: '#F8E7E7', // Soft light red background
|
||||
border: `1px solid #D83A3A`, // Prominent red border
|
||||
borderRadius: defaultRadius,
|
||||
maxWidth: '90%',
|
||||
minWidth: '90%',
|
||||
alignSelf: 'center',
|
||||
color: '#8B2525',
|
||||
color: '#8B2525', // Deep red text for good contrast
|
||||
padding: '10px 16px',
|
||||
boxShadow: '0 1px 3px rgba(216, 58, 58, 0.15)',
|
||||
boxShadow: '0 1px 3px rgba(216, 58, 58, 0.15)', // Subtle shadow with red tint
|
||||
},
|
||||
'fact-check': 'qualifications',
|
||||
'job-description': 'content',
|
||||
'job-requirements': 'qualifications',
|
||||
info: {
|
||||
'info': {
|
||||
...defaultStyle,
|
||||
backgroundColor: '#BFD8D8',
|
||||
border: `1px solid ${theme.palette.secondary.main}`,
|
||||
backgroundColor: '#BFD8D8', // Softened Dusty Teal
|
||||
border: `1px solid ${theme.palette.secondary.main}`, // Dusty Teal
|
||||
borderRadius: defaultRadius,
|
||||
color: theme.palette.text.primary,
|
||||
color: theme.palette.text.primary, // Charcoal Black (#2E2E2E) — much better contrast
|
||||
opacity: 0.95,
|
||||
},
|
||||
processing: 'status',
|
||||
qualifications: {
|
||||
'processing': "status",
|
||||
'qualifications': {
|
||||
...defaultStyle,
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
border: `1px solid ${theme.palette.secondary.main}`,
|
||||
borderRadius: `${defaultRadius} ${defaultRadius} ${defaultRadius} 0`,
|
||||
color: theme.palette.primary.contrastText,
|
||||
backgroundColor: theme.palette.primary.light, // Lighter shade, e.g., Soft Blue (#2A3B56)
|
||||
border: `1px solid ${theme.palette.secondary.main}`, // Keep Dusty Teal (#4A7A7D) for contrast
|
||||
borderRadius: `${defaultRadius} ${defaultRadius} ${defaultRadius} 0`, // Unchanged
|
||||
color: theme.palette.primary.contrastText, // Warm Gray (#D3CDBF) for readable text
|
||||
},
|
||||
resume: 'content',
|
||||
searching: 'status',
|
||||
status: {
|
||||
'resume': 'content',
|
||||
'searching': 'status',
|
||||
'status': {
|
||||
...defaultStyle,
|
||||
backgroundColor: 'rgba(74, 122, 125, 0.15)',
|
||||
border: `1px solid ${theme.palette.secondary.light}`,
|
||||
backgroundColor: 'rgba(74, 122, 125, 0.15)', // Translucent dusty teal
|
||||
border: `1px solid ${theme.palette.secondary.light}`, // Lighter dusty teal
|
||||
borderRadius: '4px',
|
||||
maxWidth: '75%',
|
||||
minWidth: '75%',
|
||||
alignSelf: 'center',
|
||||
color: theme.palette.secondary.dark,
|
||||
fontWeight: 500,
|
||||
fontSize: '0.95rem',
|
||||
color: theme.palette.secondary.dark, // Darker dusty teal for text
|
||||
fontWeight: 500, // Slightly bolder than normal
|
||||
fontSize: '0.95rem', // Slightly smaller
|
||||
padding: '8px 12px',
|
||||
opacity: 0.9,
|
||||
transition: 'opacity 0.3s ease-in-out',
|
||||
transition: 'opacity 0.3s ease-in-out', // Smooth fade effect for appearing/disappearing
|
||||
},
|
||||
streaming: 'assistant',
|
||||
system: {
|
||||
'streaming': "assistant",
|
||||
'system': {
|
||||
...defaultStyle,
|
||||
backgroundColor: '#EDEAE0',
|
||||
border: `1px dashed ${theme.palette.custom.highlight}`,
|
||||
backgroundColor: '#EDEAE0', // Soft warm gray that plays nice with #D3CDBF
|
||||
border: `1px dashed ${theme.palette.custom.highlight}`, // Golden Ochre
|
||||
borderRadius: defaultRadius,
|
||||
maxWidth: '90%',
|
||||
minWidth: '90%',
|
||||
alignSelf: 'center',
|
||||
color: theme.palette.text.primary,
|
||||
color: theme.palette.text.primary, // Charcoal Black
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
thinking: 'status',
|
||||
user: {
|
||||
'thinking': "status",
|
||||
'user': {
|
||||
...defaultStyle,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
border: `1px solid ${theme.palette.custom.highlight}`,
|
||||
borderRadius: `${defaultRadius} ${defaultRadius} 0 ${defaultRadius}`,
|
||||
alignSelf: 'flex-end',
|
||||
color: theme.palette.primary.main,
|
||||
backgroundColor: theme.palette.background.default, // Warm Gray (#D3CDBF)
|
||||
border: `1px solid ${theme.palette.custom.highlight}`, // Golden Ochre (#D4A017)
|
||||
borderRadius: `${defaultRadius} ${defaultRadius} 0 ${defaultRadius}`, // Rounded, flat bottom-right for user
|
||||
alignSelf: 'flex-end', // Right-aligned for user
|
||||
color: theme.palette.primary.main, // Midnight Blue (#1A2536) for text
|
||||
},
|
||||
};
|
||||
|
||||
// Resolve string references in styles
|
||||
for (const [key, value] of Object.entries(styles)) {
|
||||
if (typeof value === 'string') {
|
||||
styles[key] = styles[value];
|
||||
if (typeof (value) === "string") {
|
||||
(styles as any)[key] = styles[value];
|
||||
}
|
||||
}
|
||||
|
||||
const icons: any = {
|
||||
error: <ErrorOutline color="error" />,
|
||||
info: <InfoOutline color="info" />,
|
||||
processing: <LocationSearchingIcon />,
|
||||
searching: <Memory />,
|
||||
thinking: <Psychology />,
|
||||
tooling: <LocationSearchingIcon />,
|
||||
"error": <ErrorOutline color='error' />,
|
||||
"info": <InfoOutline color='info' />,
|
||||
"processing": <LocationSearchingIcon />,
|
||||
// "streaming": <Stream />,
|
||||
"searching": <Memory />,
|
||||
"thinking": <Psychology />,
|
||||
"tooling": <LocationSearchingIcon />,
|
||||
};
|
||||
|
||||
// Render Accordion for expandable content
|
||||
if (expandable || (role === 'content' && title)) {
|
||||
// Determine if Accordion is controlled
|
||||
const isControlled = typeof expanded === 'boolean' && typeof onExpand === 'function';
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
expanded={isControlled ? expanded : undefined} // Omit expanded prop for uncontrolled
|
||||
defaultExpanded={expanded} // Default to collapsed for uncontrolled Accordion
|
||||
expanded={expanded}
|
||||
className={className}
|
||||
onChange={(_event, newExpanded) => {
|
||||
if (isControlled && onExpand) {
|
||||
onExpand(newExpanded); // Call onExpand with new state
|
||||
}
|
||||
}}
|
||||
onChange={() => { onExpand && onExpand(); setExpanded(!expanded); }}
|
||||
sx={{ ...styles[role], ...sx }}
|
||||
>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
slotProps={{
|
||||
content: {
|
||||
sx: {
|
||||
fontWeight: 'bold',
|
||||
fontSize: '1.1rem',
|
||||
m: 0,
|
||||
p: 0,
|
||||
display: 'flex',
|
||||
justifyItems: 'center',
|
||||
},
|
||||
},
|
||||
}}
|
||||
slotProps={{ content: { sx: { fontWeight: 'bold', fontSize: '1.1rem', m: 0, p: 0, display: 'flex', justifyItems: 'center' } } }}
|
||||
>
|
||||
{title || ''}
|
||||
{title || ""}
|
||||
</AccordionSummary>
|
||||
<AccordionDetails sx={{ mt: 0, mb: 0, p: 0, pl: 2, pr: 2 }}>
|
||||
{children}
|
||||
@ -198,20 +178,10 @@ function ChatBubble(props: ChatBubbleProps) {
|
||||
);
|
||||
}
|
||||
|
||||
// Render non-expandable content
|
||||
return (
|
||||
<Box
|
||||
className={className}
|
||||
sx={{
|
||||
...(role in styles ? styles[role] : styles['status']),
|
||||
gap: 1,
|
||||
display: 'flex',
|
||||
...sx,
|
||||
flexDirection: 'row',
|
||||
}}
|
||||
>
|
||||
<Box className={className} sx={{ ...(role in styles ? styles[role] : styles["status"]), gap: 1, display: "flex", ...sx, flexDirection: "row" }}>
|
||||
{icons[role] !== undefined && icons[role]}
|
||||
<Box sx={{ p: 0, m: 0, gap: 0, display: 'flex', flexGrow: 1, flexDirection: 'column' }}>
|
||||
<Box sx={{ p: 0, m: 0, gap: 0, display: "flex", flexGrow: 1, flexDirection: "column" }}>
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
|
@ -7,12 +7,10 @@ type QueryOptions = {
|
||||
enable_context?: boolean,
|
||||
};
|
||||
|
||||
type ChatSubmitQueryInterface = (prompt: string, tunables?: QueryOptions) => void;
|
||||
|
||||
interface ChatQueryInterface {
|
||||
prompt: string,
|
||||
tunables?: QueryOptions,
|
||||
submitQuery?: ChatSubmitQueryInterface
|
||||
submitQuery?: (prompt: string, tunables?: QueryOptions) => void
|
||||
}
|
||||
|
||||
const ChatQuery = (props : ChatQueryInterface) => {
|
||||
@ -41,7 +39,6 @@ const ChatQuery = (props : ChatQueryInterface) => {
|
||||
export type {
|
||||
ChatQueryInterface,
|
||||
QueryOptions,
|
||||
ChatSubmitQueryInterface,
|
||||
};
|
||||
|
||||
export {
|
||||
|
@ -14,9 +14,7 @@ import Typography from '@mui/material/Typography';
|
||||
// import ResetIcon from '@mui/icons-material/History';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
|
||||
import { connectionBase } from './Global';
|
||||
import { BackstoryPageProps } from './BackstoryTab';
|
||||
import { restyle } from 'plotly.js';
|
||||
import { SetSnackType } from './Snack';
|
||||
|
||||
interface ServerTunables {
|
||||
system_prompt: string,
|
||||
@ -34,6 +32,12 @@ type Tool = {
|
||||
returns?: any
|
||||
};
|
||||
|
||||
interface ControlsParams {
|
||||
connectionBase: string,
|
||||
sessionId: string | undefined,
|
||||
setSnack: SetSnackType,
|
||||
};
|
||||
|
||||
type GPUInfo = {
|
||||
name: string,
|
||||
memory: number,
|
||||
@ -86,7 +90,7 @@ const SystemInfoComponent: React.FC<{ systemInfo: SystemInfo | undefined }> = ({
|
||||
return <div className="SystemInfo">{systemElements}</div>;
|
||||
};
|
||||
|
||||
const ControlsPage = ({ sessionId, setSnack }: BackstoryPageProps) => {
|
||||
const Controls = ({ sessionId, setSnack, connectionBase }: ControlsParams) => {
|
||||
const [editSystemPrompt, setEditSystemPrompt] = useState<string>("");
|
||||
const [systemInfo, setSystemInfo] = useState<SystemInfo | undefined>(undefined);
|
||||
const [tools, setTools] = useState<Tool[]>([]);
|
||||
@ -122,7 +126,7 @@ const ControlsPage = ({ sessionId, setSnack }: BackstoryPageProps) => {
|
||||
|
||||
sendSystemPrompt(systemPrompt);
|
||||
|
||||
}, [systemPrompt, sessionId, setSnack, serverTunables]);
|
||||
}, [systemPrompt, connectionBase, sessionId, setSnack, serverTunables]);
|
||||
|
||||
useEffect(() => {
|
||||
if (serverTunables === undefined || messageHistoryLength === serverTunables.message_history_length || !messageHistoryLength || sessionId === undefined) {
|
||||
@ -153,7 +157,7 @@ const ControlsPage = ({ sessionId, setSnack }: BackstoryPageProps) => {
|
||||
|
||||
sendMessageHistoryLength(messageHistoryLength);
|
||||
|
||||
}, [messageHistoryLength, setMessageHistoryLength, sessionId, setSnack, serverTunables]);
|
||||
}, [messageHistoryLength, setMessageHistoryLength, connectionBase, sessionId, setSnack, serverTunables]);
|
||||
|
||||
const reset = async (types: ("rags" | "tools" | "history" | "system_prompt" | "message_history_length")[], message: string = "Update successful.") => {
|
||||
try {
|
||||
@ -216,7 +220,7 @@ const ControlsPage = ({ sessionId, setSnack }: BackstoryPageProps) => {
|
||||
console.error('Error obtaining system information:', error);
|
||||
setSnack("Unable to obtain system information.", "error");
|
||||
});
|
||||
}, [systemInfo, setSystemInfo, setSnack, sessionId])
|
||||
}, [systemInfo, setSystemInfo, connectionBase, setSnack, sessionId])
|
||||
|
||||
useEffect(() => {
|
||||
setEditSystemPrompt(systemPrompt.trim());
|
||||
@ -290,7 +294,7 @@ const ControlsPage = ({ sessionId, setSnack }: BackstoryPageProps) => {
|
||||
}
|
||||
|
||||
fetchTunables();
|
||||
}, [sessionId, setServerTunables, setSystemPrompt, setMessageHistoryLength, serverTunables, setTools, setRags]);
|
||||
}, [sessionId, connectionBase, setServerTunables, setSystemPrompt, setMessageHistoryLength, serverTunables, setTools, setRags]);
|
||||
|
||||
const toggle = async (type: string, index: number) => {
|
||||
switch (type) {
|
||||
@ -429,6 +433,11 @@ const ControlsPage = ({ sessionId, setSnack }: BackstoryPageProps) => {
|
||||
</div>);
|
||||
}
|
||||
|
||||
|
||||
export type {
|
||||
ControlsParams
|
||||
};
|
||||
|
||||
export {
|
||||
ControlsPage
|
||||
Controls
|
||||
};
|
@ -8,14 +8,13 @@ import { SxProps, Theme } from '@mui/material';
|
||||
import PropagateLoader from "react-spinners/PropagateLoader";
|
||||
|
||||
import { Message, MessageList, MessageData } from './Message';
|
||||
import { SetSnackType } from './Snack';
|
||||
import { ContextStatus } from './ContextStatus';
|
||||
import { Scrollable } from './Scrollable';
|
||||
import { DeleteConfirmation } from './DeleteConfirmation';
|
||||
import { QueryOptions } from './ChatQuery';
|
||||
import './Conversation.css';
|
||||
import { BackstoryTextField } from './BackstoryTextField';
|
||||
import { BackstoryElementProps } from './BackstoryTab';
|
||||
import { connectionBase } from './Global';
|
||||
|
||||
const loadingMessage: MessageData = { "role": "status", "content": "Establishing connection with server..." };
|
||||
|
||||
@ -45,7 +44,7 @@ interface BackstoryMessage {
|
||||
timestamp: string;
|
||||
};
|
||||
|
||||
interface ConversationProps extends BackstoryElementProps {
|
||||
interface ConversationProps {
|
||||
className?: string, // Override default className
|
||||
type: ConversationMode, // Type of Conversation chat
|
||||
prompt?: string, // Prompt to display in TextField input
|
||||
@ -53,6 +52,9 @@ interface ConversationProps extends BackstoryElementProps {
|
||||
resetAction?: () => void, // Callback when Reset is pressed
|
||||
multiline?: boolean, // Render TextField as multiline or not
|
||||
resetLabel?: string, // Label to put on Reset button
|
||||
connectionBase: string, // Base URL for fetch() calls
|
||||
sessionId?: string, // Session ID for fetch() calls
|
||||
setSnack: SetSnackType, // Callback to display snack popups
|
||||
defaultPrompts?: React.ReactElement[], // Set of Elements to display after the TextField
|
||||
defaultQuery?: string, // Default text to populate the TextField input
|
||||
emptyPrompt?: string, // If input is not shown and an action is taken, send this prompt
|
||||
@ -66,26 +68,26 @@ interface ConversationProps extends BackstoryElementProps {
|
||||
};
|
||||
|
||||
const Conversation = forwardRef<ConversationHandle, ConversationProps>(({
|
||||
actionLabel,
|
||||
className,
|
||||
defaultPrompts,
|
||||
defaultQuery,
|
||||
type,
|
||||
prompt,
|
||||
emptyPrompt,
|
||||
actionLabel,
|
||||
resetAction,
|
||||
multiline,
|
||||
resetLabel,
|
||||
connectionBase,
|
||||
sessionId,
|
||||
setSnack,
|
||||
defaultPrompts,
|
||||
hideDefaultPrompts,
|
||||
defaultQuery,
|
||||
preamble,
|
||||
hidePreamble,
|
||||
messageFilter,
|
||||
messages,
|
||||
multiline,
|
||||
onResponse,
|
||||
prompt,
|
||||
preamble,
|
||||
resetAction,
|
||||
resetLabel,
|
||||
sessionId,
|
||||
setSnack,
|
||||
submitQuery,
|
||||
sx,
|
||||
type,
|
||||
onResponse
|
||||
}: ConversationProps, ref) => {
|
||||
const [query, setQuery] = useState<string>("");
|
||||
const [contextUsedPercentage, setContextUsedPercentage] = useState<number>(0);
|
||||
@ -131,7 +133,7 @@ const Conversation = forwardRef<ConversationHandle, ConversationProps>(({
|
||||
}
|
||||
};
|
||||
fetchContextStatus();
|
||||
}, [setContextStatus, setSnack, sessionId, type]);
|
||||
}, [setContextStatus, connectionBase, setSnack, sessionId, type]);
|
||||
|
||||
/* Transform the 'Conversation' by filtering via callback, then adding
|
||||
* preamble and messages based on whether the conversation
|
||||
@ -211,7 +213,7 @@ const Conversation = forwardRef<ConversationHandle, ConversationProps>(({
|
||||
setSnack("Unable to obtain chat history.", "error");
|
||||
}
|
||||
};
|
||||
}, [setConversation, updateContextStatus, setSnack, type, sessionId]);
|
||||
}, [setConversation, updateContextStatus, connectionBase, setSnack, type, sessionId]);
|
||||
|
||||
// Set the initial chat history to "loading" or the welcome message if loaded.
|
||||
useEffect(() => {
|
||||
@ -511,16 +513,16 @@ const Conversation = forwardRef<ConversationHandle, ConversationProps>(({
|
||||
>
|
||||
{
|
||||
filteredConversation.map((message, index) =>
|
||||
<Message key={index} expanded={message.expanded === undefined ? true : message.expanded} {...{ sendQuery, message, connectionBase, sessionId, setSnack, submitQuery }} />
|
||||
<Message key={index} {...{ sendQuery, message, connectionBase, sessionId, setSnack }} />
|
||||
)
|
||||
}
|
||||
{
|
||||
processingMessage !== undefined &&
|
||||
<Message {...{ sendQuery, connectionBase, sessionId, setSnack, message: processingMessage, submitQuery }} />
|
||||
<Message {...{ sendQuery, connectionBase, sessionId, setSnack, message: processingMessage }} />
|
||||
}
|
||||
{
|
||||
streamingMessage !== undefined &&
|
||||
<Message {...{ sendQuery, connectionBase, sessionId, setSnack, message: streamingMessage, submitQuery }} />
|
||||
<Message {...{ sendQuery, connectionBase, sessionId, setSnack, message: streamingMessage }} />
|
||||
}
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
|
@ -1,19 +1,21 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Message } from './Message';
|
||||
import { BackstoryElementProps } from './BackstoryTab';
|
||||
import { connectionBase } from './Global';
|
||||
import { Message, MessageSubmitQuery } from './Message';
|
||||
import { SetSnackType } from './Snack';
|
||||
|
||||
interface DocumentProps extends BackstoryElementProps {
|
||||
interface DocumentProps {
|
||||
title: string;
|
||||
expanded?: boolean;
|
||||
filepath: string;
|
||||
content?: string;
|
||||
setSnack: SetSnackType;
|
||||
submitQuery?: MessageSubmitQuery;
|
||||
connectionBase: string;
|
||||
disableCopy?: boolean;
|
||||
onExpand?: (open: boolean) => void;
|
||||
onExpand?: () => void;
|
||||
}
|
||||
|
||||
const Document = (props: DocumentProps) => {
|
||||
const { setSnack, submitQuery, filepath, content, title, expanded, disableCopy, onExpand, sessionId } = props;
|
||||
const { setSnack, submitQuery, connectionBase, filepath, content, title, expanded, disableCopy, onExpand } = props;
|
||||
|
||||
const [document, setDocument] = useState<string>("");
|
||||
|
||||
@ -61,7 +63,6 @@ const Document = (props: DocumentProps) => {
|
||||
expanded,
|
||||
disableCopy,
|
||||
onExpand,
|
||||
sessionId,
|
||||
}} />
|
||||
);
|
||||
};
|
||||
|
@ -1,13 +0,0 @@
|
||||
const getConnectionBase = (loc: any): string => {
|
||||
if (!loc.host.match(/.*battle-linux.*/)) {
|
||||
return loc.protocol + "//" + loc.host;
|
||||
} else {
|
||||
return loc.protocol + "//battle-linux.ketrenos.com:8912";
|
||||
}
|
||||
}
|
||||
|
||||
const connectionBase = getConnectionBase(window.location);
|
||||
|
||||
export {
|
||||
connectionBase
|
||||
};
|
@ -1,69 +0,0 @@
|
||||
import React, { forwardRef } from 'react';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import Box from '@mui/material/Box';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import MuiMarkdown from 'mui-markdown';
|
||||
|
||||
import { BackstoryPageProps } from './BackstoryTab';
|
||||
import { Conversation, ConversationHandle } from './Conversation';
|
||||
import { ChatQuery } from './ChatQuery';
|
||||
import { MessageList } from './Message';
|
||||
|
||||
const HomePage = forwardRef<ConversationHandle, BackstoryPageProps>((props: BackstoryPageProps, ref) => {
|
||||
const { sessionId, setSnack, submitQuery } = props;
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||||
|
||||
const backstoryPreamble: MessageList = [
|
||||
{
|
||||
role: 'content',
|
||||
title: 'Welcome to Backstory',
|
||||
disableCopy: true,
|
||||
content: `
|
||||
Backstory is a RAG enabled expert system with access to real-time data running self-hosted
|
||||
(no cloud) versions of industry leading Large and Small Language Models (LLM/SLMs).
|
||||
It was written by James Ketrenos in order to provide answers to
|
||||
questions potential employers may have about his work history.
|
||||
|
||||
What would you like to know about James?
|
||||
`,
|
||||
}
|
||||
];
|
||||
|
||||
const backstoryQuestions = [
|
||||
<Box sx={{ display: "flex", flexDirection: isMobile ? "column" : "row" }}>
|
||||
<ChatQuery prompt="What is James Ketrenos' work history?" tunables={{ enable_tools: false }} submitQuery={submitQuery} />
|
||||
<ChatQuery prompt="What programming languages has James used?" tunables={{ enable_tools: false }} submitQuery={submitQuery} />
|
||||
<ChatQuery prompt="What are James' professional strengths?" tunables={{ enable_tools: false }} submitQuery={submitQuery} />
|
||||
<ChatQuery prompt="What are today's headlines on CNBC.com?" tunables={{ enable_tools: true, enable_rag: false, enable_context: false }} submitQuery={submitQuery} />
|
||||
</Box>,
|
||||
<Box sx={{ p: 1 }}>
|
||||
<MuiMarkdown>
|
||||
As with all LLM interactions, the results may not be 100% accurate. If you have questions about my career,
|
||||
I'd love to hear from you. You can send me an email at **james_backstory@ketrenos.com**.
|
||||
</MuiMarkdown>
|
||||
</Box>
|
||||
];
|
||||
|
||||
return <Conversation
|
||||
sx={{
|
||||
maxWidth: "1024px",
|
||||
height: "calc(100vh - 72px)",
|
||||
}}
|
||||
ref={ref}
|
||||
{...{
|
||||
type: "chat",
|
||||
prompt: "What would you like to know about James?",
|
||||
resetLabel: "chat",
|
||||
sessionId,
|
||||
setSnack,
|
||||
preamble: backstoryPreamble,
|
||||
defaultPrompts: backstoryQuestions,
|
||||
submitQuery,
|
||||
}}
|
||||
/>;
|
||||
});
|
||||
|
||||
export {
|
||||
HomePage
|
||||
};
|
@ -28,7 +28,6 @@ import { VectorVisualizer } from './VectorVisualizer';
|
||||
import { SetSnackType } from './Snack';
|
||||
import { CopyBubble } from './CopyBubble';
|
||||
import { Scrollable } from './Scrollable';
|
||||
import { BackstoryElementProps } from './BackstoryTab';
|
||||
|
||||
type MessageRoles =
|
||||
'assistant' |
|
||||
@ -84,13 +83,19 @@ interface MessageMetaData {
|
||||
setSnack: SetSnackType,
|
||||
}
|
||||
|
||||
type MessageSubmitQuery = (text: string) => void;
|
||||
|
||||
type MessageList = MessageData[];
|
||||
|
||||
interface MessageProps extends BackstoryElementProps {
|
||||
interface MessageProps {
|
||||
sx?: SxProps<Theme>,
|
||||
message: MessageData,
|
||||
expanded?: boolean,
|
||||
onExpand?: (open: boolean) => void,
|
||||
// expanded?: boolean, // Provided as part of MessageData
|
||||
onExpand?: () => void,
|
||||
submitQuery?: MessageSubmitQuery,
|
||||
sessionId?: string,
|
||||
connectionBase: string,
|
||||
setSnack: SetSnackType,
|
||||
className?: string,
|
||||
};
|
||||
|
||||
@ -237,12 +242,12 @@ const MessageMeta = (props: MessageMetaProps) => {
|
||||
};
|
||||
|
||||
const Message = (props: MessageProps) => {
|
||||
const { message, submitQuery, sx, className, onExpand, expanded, sessionId, setSnack } = props;
|
||||
const [metaExpanded, setMetaExpanded] = useState<boolean>(false);
|
||||
const { message, submitQuery, sx, className, onExpand } = props;
|
||||
const [expanded, setExpanded] = useState<boolean>(false);
|
||||
const textFieldRef = useRef(null);
|
||||
|
||||
const handleMetaExpandClick = () => {
|
||||
setMetaExpanded(!metaExpanded);
|
||||
const handleExpandClick = () => {
|
||||
setExpanded(!expanded);
|
||||
};
|
||||
|
||||
if (message === undefined) {
|
||||
@ -261,7 +266,6 @@ const Message = (props: MessageProps) => {
|
||||
className={className || "Message"}
|
||||
{...message}
|
||||
onExpand={onExpand}
|
||||
expanded={expanded}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
@ -287,7 +291,7 @@ const Message = (props: MessageProps) => {
|
||||
overflow: "auto", /* Handles scrolling for the div */
|
||||
}}
|
||||
>
|
||||
<StyledMarkdown {...{ content: formattedContent, submitQuery, sessionId, setSnack }} />
|
||||
<StyledMarkdown {...{ content: formattedContent, submitQuery }} />
|
||||
</Scrollable>
|
||||
:
|
||||
<Typography
|
||||
@ -303,12 +307,12 @@ const Message = (props: MessageProps) => {
|
||||
{(message.disableCopy === undefined || message.disableCopy === false) && ["assistant", "content"].includes(message.role) && <CopyBubble content={message.content} />}
|
||||
{message.metadata && (
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
|
||||
<Button variant="text" onClick={handleMetaExpandClick} sx={{ color: "darkgrey", p: 0 }}>
|
||||
<Button variant="text" onClick={handleExpandClick} sx={{ color: "darkgrey", p: 0 }}>
|
||||
LLM information for this query
|
||||
</Button>
|
||||
<ExpandMore
|
||||
expand={metaExpanded}
|
||||
onClick={handleMetaExpandClick}
|
||||
expand={expanded}
|
||||
onClick={handleExpandClick}
|
||||
aria-expanded={expanded}
|
||||
aria-label="show more"
|
||||
>
|
||||
@ -318,7 +322,7 @@ const Message = (props: MessageProps) => {
|
||||
)}
|
||||
</CardActions>
|
||||
{message.metadata && <>
|
||||
<Collapse in={metaExpanded} timeout="auto" unmountOnExit>
|
||||
<Collapse in={expanded} timeout="auto" unmountOnExit>
|
||||
<CardContent>
|
||||
<MessageMeta messageProps={props} metadata={message.metadata} />
|
||||
</CardContent>
|
||||
@ -333,6 +337,7 @@ export type {
|
||||
MessageList,
|
||||
MessageData,
|
||||
MessageRoles,
|
||||
MessageSubmitQuery
|
||||
};
|
||||
|
||||
export {
|
||||
|
@ -4,14 +4,21 @@ import {
|
||||
Tab,
|
||||
Box,
|
||||
} from '@mui/material';
|
||||
import { SxProps } from '@mui/material';
|
||||
import { SxProps, Theme } from '@mui/material';
|
||||
|
||||
import { ChatQuery } from './ChatQuery';
|
||||
import { MessageList, MessageData } from './Message';
|
||||
import { SetSnackType } from './Snack';
|
||||
import { Conversation } from './Conversation';
|
||||
import { BackstoryPageProps } from './BackstoryTab';
|
||||
|
||||
import './ResumeBuilderPage.css';
|
||||
import './ResumeBuilder.css';
|
||||
|
||||
interface ResumeBuilderProps {
|
||||
connectionBase: string,
|
||||
sessionId: string | undefined,
|
||||
setSnack: SetSnackType,
|
||||
sx?: SxProps<Theme>;
|
||||
};
|
||||
|
||||
/**
|
||||
* ResumeBuilder component
|
||||
@ -19,11 +26,12 @@ import './ResumeBuilderPage.css';
|
||||
* A responsive component that displays job descriptions, generated resumes and fact checks
|
||||
* with different layouts for mobile and desktop views.
|
||||
*/
|
||||
const ResumeBuilderPage: React.FC<BackstoryPageProps> = ({
|
||||
const ResumeBuilder: React.FC<ResumeBuilderProps> = ({
|
||||
sx,
|
||||
connectionBase,
|
||||
sessionId,
|
||||
setSnack,
|
||||
submitQuery,
|
||||
setSnack
|
||||
|
||||
}) => {
|
||||
// State for editing job description
|
||||
const [hasJobDescription, setHasJobDescription] = useState<boolean>(false);
|
||||
@ -79,7 +87,7 @@ const ResumeBuilderPage: React.FC<BackstoryPageProps> = ({
|
||||
messages[3].role = 'job-requirements';
|
||||
messages[3].title = 'Job Requirements';
|
||||
messages[3].disableCopy = false;
|
||||
messages[3].expanded = false;
|
||||
messages[3].expanded = true;
|
||||
messages[3].expandable = true;
|
||||
}
|
||||
|
||||
@ -128,7 +136,7 @@ const ResumeBuilderPage: React.FC<BackstoryPageProps> = ({
|
||||
messages[1].role = 'fact-check';
|
||||
messages[1].title = 'Fact Check';
|
||||
messages[1].disableCopy = false;
|
||||
messages[1].expanded = true;
|
||||
messages[1].expanded = false;
|
||||
messages[1].expandable = true;
|
||||
}
|
||||
|
||||
@ -217,8 +225,8 @@ const ResumeBuilderPage: React.FC<BackstoryPageProps> = ({
|
||||
resetAction: resetJobDescription,
|
||||
onResponse: jobResponse,
|
||||
sessionId,
|
||||
connectionBase,
|
||||
setSnack,
|
||||
submitQuery,
|
||||
sx,
|
||||
}}
|
||||
/>
|
||||
@ -236,13 +244,13 @@ const ResumeBuilderPage: React.FC<BackstoryPageProps> = ({
|
||||
resetAction: resetJobDescription,
|
||||
onResponse: jobResponse,
|
||||
sessionId,
|
||||
connectionBase,
|
||||
setSnack,
|
||||
submitQuery,
|
||||
sx,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
}, [filterJobDescriptionMessages, hasJobDescription, sessionId, setSnack, jobResponse, resetJobDescription, hasFacts, hasResume, submitQuery]);
|
||||
}, [connectionBase, filterJobDescriptionMessages, hasJobDescription, sessionId, setSnack, jobResponse, resetJobDescription, hasFacts, hasResume]);
|
||||
|
||||
/**
|
||||
* Renders the resume view with loading indicator
|
||||
@ -267,8 +275,8 @@ const ResumeBuilderPage: React.FC<BackstoryPageProps> = ({
|
||||
onResponse: resumeResponse,
|
||||
resetAction: resetResume,
|
||||
sessionId,
|
||||
connectionBase,
|
||||
setSnack,
|
||||
submitQuery,
|
||||
sx,
|
||||
}}
|
||||
/>
|
||||
@ -284,14 +292,14 @@ const ResumeBuilderPage: React.FC<BackstoryPageProps> = ({
|
||||
onResponse: resumeResponse,
|
||||
resetAction: resetResume,
|
||||
sessionId,
|
||||
connectionBase,
|
||||
setSnack,
|
||||
defaultPrompts: resumeQuestions,
|
||||
submitQuery,
|
||||
sx,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
}, [filterResumeMessages, hasFacts, sessionId, setSnack, resumeResponse, resetResume, hasResume, submitQuery]);
|
||||
}, [connectionBase, filterResumeMessages, hasFacts, sessionId, setSnack, resumeResponse, resetResume, hasResume]);
|
||||
|
||||
/**
|
||||
* Renders the fact check view
|
||||
@ -315,12 +323,12 @@ const ResumeBuilderPage: React.FC<BackstoryPageProps> = ({
|
||||
resetAction: resetFacts,
|
||||
onResponse: factsResponse,
|
||||
sessionId,
|
||||
submitQuery,
|
||||
connectionBase,
|
||||
setSnack,
|
||||
sx,
|
||||
}}
|
||||
/>
|
||||
}, [ sessionId, setSnack, factsResponse, filterFactsMessages, resetFacts, hasResume, hasFacts, submitQuery]);
|
||||
}, [connectionBase, sessionId, setSnack, factsResponse, filterFactsMessages, resetFacts, hasResume, hasFacts]);
|
||||
|
||||
return (
|
||||
<Box className="ResumeBuilder"
|
||||
@ -361,7 +369,11 @@ const ResumeBuilderPage: React.FC<BackstoryPageProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export {
|
||||
ResumeBuilderPage
|
||||
export type {
|
||||
ResumeBuilderProps
|
||||
};
|
||||
|
||||
export {
|
||||
ResumeBuilder
|
||||
};
|
||||
|
@ -1,6 +0,0 @@
|
||||
.ResumeBuilder .JsonViewScrollable {
|
||||
min-height: unset !important;
|
||||
max-height: 30rem !important;
|
||||
border: 1px solid orange;
|
||||
overflow-x: auto !important;
|
||||
}
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { MuiMarkdown } from 'mui-markdown';
|
||||
import { SxProps, useTheme } from '@mui/material/styles';
|
||||
import { Link } from '@mui/material';
|
||||
import { ChatQuery } from './ChatQuery';
|
||||
import { ChatQuery, QueryOptions } from './ChatQuery';
|
||||
import Box from '@mui/material/Box';
|
||||
import JsonView from '@uiw/react-json-view';
|
||||
import { vscodeTheme } from '@uiw/react-json-view/vscode';
|
||||
@ -11,12 +11,12 @@ import { Scrollable } from './Scrollable';
|
||||
import { jsonrepair } from 'jsonrepair';
|
||||
|
||||
import './StyledMarkdown.css';
|
||||
import { BackstoryElementProps } from './BackstoryTab';
|
||||
|
||||
interface StyledMarkdownProps extends BackstoryElementProps {
|
||||
interface StyledMarkdownProps {
|
||||
className?: string,
|
||||
content: string,
|
||||
sx?: SxProps,
|
||||
submitQuery?: (prompt: string, tunables?: QueryOptions) => void,
|
||||
};
|
||||
|
||||
const StyledMarkdown: React.FC<StyledMarkdownProps> = (props: StyledMarkdownProps) => {
|
||||
@ -31,13 +31,10 @@ const StyledMarkdown: React.FC<StyledMarkdownProps> = (props: StyledMarkdownProp
|
||||
if (className === "lang-mermaid") {
|
||||
return <Mermaid className="Mermaid" chart={content} />;
|
||||
}
|
||||
if (className === "lang-markdown") {
|
||||
return <MuiMarkdown children={content} />;
|
||||
}
|
||||
if (className === "lang-json") {
|
||||
try {
|
||||
const fixed = jsonrepair(content);
|
||||
return <Scrollable autoscroll className="JsonViewScrollable">
|
||||
return <Scrollable className="JsonViewScrollable">
|
||||
<JsonView
|
||||
className="JsonView"
|
||||
style={{
|
||||
@ -53,7 +50,6 @@ const StyledMarkdown: React.FC<StyledMarkdownProps> = (props: StyledMarkdownProp
|
||||
displayDataTypes={false}
|
||||
objectSortKeys={false}
|
||||
collapsed={false}
|
||||
shortenTextAfterLength={100}
|
||||
value={JSON.parse(fixed)}>
|
||||
<JsonView.String
|
||||
render={({ children, ...reset }) => {
|
||||
@ -85,13 +81,17 @@ const StyledMarkdown: React.FC<StyledMarkdownProps> = (props: StyledMarkdownProp
|
||||
}
|
||||
}
|
||||
},
|
||||
ChatQuery: {
|
||||
chatQuery: undefined
|
||||
};
|
||||
|
||||
if (submitQuery) {
|
||||
overrides.ChatQuery = {
|
||||
component: ChatQuery,
|
||||
props: {
|
||||
submitQuery,
|
||||
},
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
return <Box
|
||||
className={`MuiMarkdown ${className || ""}`}
|
||||
|
@ -8,21 +8,16 @@ import Button from '@mui/material/Button';
|
||||
import SendIcon from '@mui/icons-material/Send';
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import Switch from '@mui/material/Switch';
|
||||
import { SxProps, Theme } from '@mui/material';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import JsonView from '@uiw/react-json-view';
|
||||
|
||||
import { SetSnackType } from './Snack';
|
||||
import { Scrollable } from './Scrollable';
|
||||
import { StyledMarkdown } from './StyledMarkdown';
|
||||
import { connectionBase } from './Global';
|
||||
|
||||
import './VectorVisualizer.css';
|
||||
import { BackstoryPageProps } from './BackstoryTab';
|
||||
|
||||
interface VectorVisualizerProps extends BackstoryPageProps {
|
||||
inline?: boolean;
|
||||
rag?: any;
|
||||
};
|
||||
|
||||
interface Metadata {
|
||||
doc_type?: string;
|
||||
@ -51,8 +46,14 @@ interface PlotData {
|
||||
layout: Partial<Plotly.Layout>;
|
||||
}
|
||||
|
||||
|
||||
|
||||
interface VectorVisualizerProps {
|
||||
connectionBase: string;
|
||||
sessionId?: string;
|
||||
setSnack: SetSnackType;
|
||||
inline?: boolean;
|
||||
rag?: any;
|
||||
sx?: SxProps<Theme>;
|
||||
}
|
||||
|
||||
interface ChromaResult {
|
||||
distances: number[];
|
||||
@ -108,7 +109,7 @@ const symbolMap: Record<string, string> = {
|
||||
};
|
||||
|
||||
const VectorVisualizer: React.FC<VectorVisualizerProps> = (props: VectorVisualizerProps) => {
|
||||
const { setSnack, rag, inline, sessionId, sx, submitQuery } = props;
|
||||
const { setSnack, rag, inline, connectionBase, sessionId, sx } = props;
|
||||
const [plotData, setPlotData] = useState<PlotData | null>(null);
|
||||
const [newQuery, setNewQuery] = useState<string>('');
|
||||
const [newQueryEmbedding, setNewQueryEmbedding] = useState<ChromaResult | undefined>(undefined);
|
||||
@ -150,7 +151,7 @@ const VectorVisualizer: React.FC<VectorVisualizerProps> = (props: VectorVisualiz
|
||||
};
|
||||
|
||||
fetchCollection();
|
||||
}, [result, setResult, setSnack, sessionId, view2D])
|
||||
}, [result, setResult, connectionBase, setSnack, sessionId, view2D])
|
||||
|
||||
useEffect(() => {
|
||||
if (!result || !result.embeddings) return;
|
||||
@ -436,7 +437,7 @@ const VectorVisualizer: React.FC<VectorVisualizerProps> = (props: VectorVisualiz
|
||||
wordBreak: 'break-all',
|
||||
}}
|
||||
>
|
||||
<StyledMarkdown sx={{ p: 1, pt: 0 }} content={tooltip?.content || "Select a node in the visualization."} {...{ sessionId, setSnack, submitQuery }} />
|
||||
<StyledMarkdown sx={{ p: 1, pt: 0 }} content={tooltip?.content || "Select a node in the visualization."} />
|
||||
</Scrollable>
|
||||
}
|
||||
</Card>
|
||||
@ -447,7 +448,7 @@ const VectorVisualizer: React.FC<VectorVisualizerProps> = (props: VectorVisualiz
|
||||
}
|
||||
|
||||
{
|
||||
!inline &&
|
||||
!inline &&
|
||||
<Box className="Query" sx={{ display: "flex", flexDirection: "row", p: 1 }}>
|
||||
<TextField
|
||||
variant="outlined"
|
||||
@ -468,21 +469,8 @@ const VectorVisualizer: React.FC<VectorVisualizerProps> = (props: VectorVisualiz
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const VectorVisualizerPage: React.FC<VectorVisualizerProps> = (props: VectorVisualizerProps) => {
|
||||
return <Scrollable
|
||||
sx={{
|
||||
maxWidth: "1024px",
|
||||
height: "calc(100vh - 72px)",
|
||||
}}
|
||||
>
|
||||
<VectorVisualizer {...props} />
|
||||
</Scrollable>;
|
||||
};
|
||||
|
||||
export type { VectorVisualizerProps, ResultData };
|
||||
|
||||
export {
|
||||
VectorVisualizer,
|
||||
VectorVisualizerPage
|
||||
VectorVisualizer
|
||||
};
|
||||
|
@ -11,9 +11,9 @@ scrape_configs:
|
||||
insecure_skip_verify: true
|
||||
|
||||
- job_name: 'backstory-prod'
|
||||
scrape_interval: 5s
|
||||
scrape_interval: 30s
|
||||
metrics_path: /metrics
|
||||
scheme: http
|
||||
scheme: https
|
||||
static_configs:
|
||||
- targets: ['backstory-prod:8911']
|
||||
tls_config:
|
||||
|
@ -197,7 +197,6 @@ class JobDescription(Agent):
|
||||
job_requirements = message.metadata["generate_factual_tailored_resume"]["job_requirements"]["results"]
|
||||
new_message.response = f"```json\n\n{json.dumps(job_requirements, indent=2)}\n```\n"
|
||||
new_message.status = "done"
|
||||
new_message.actions = [ "job_description" ]
|
||||
self.conversation.add(new_message)
|
||||
|
||||
self.system_prompt = system_user_qualifications
|
||||
|
Loading…
x
Reference in New Issue
Block a user