From 27ac557219311d32de9bda2b933971dcae8ace0c Mon Sep 17 00:00:00 2001 From: Daniel William Frederick Rayson Date: Wed, 18 Mar 2026 13:04:45 +0000 Subject: [PATCH 1/8] corrected dates --- Rayson.CV/Api/Domain/Resources/cv.md | 12 ++++++------ Rayson.CV/UI/src/data/cv.ts | 14 +++++++------- Rayson.CV/UI/src/pages/TimelinePage.tsx | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Rayson.CV/Api/Domain/Resources/cv.md b/Rayson.CV/Api/Domain/Resources/cv.md index cec993c..9fce328 100644 --- a/Rayson.CV/Api/Domain/Resources/cv.md +++ b/Rayson.CV/Api/Domain/Resources/cv.md @@ -35,27 +35,27 @@ Has worked with continuous integration frameworks such as Octopus, Jenkins and T WORK EXPERIENCE -Freelance (Personal and Brochure Websites) 2007 – 2012 +Freelance (Personal and Brochure Websites) 2007 - 2012 Title: Website Development and Support • Design and implementation of custom websites • General site maintenance and updates • Site specification and general IT consultation -DBLogic (Software House) Spring 2012 - Spring 2014 +DBLogic (Software House) 2012 - 2014 Title: Software Development Engineer • Full development life-cycle ownership • Customer support and software bug fixing • Experience with the SCRUM method • Multiple completed business applications -ARVS ltd (Car Accident Claims Management) Summer 2014 - Summer 2016 +ARVS ltd (Car Accident Claims Management) 2014 - 2016 Title: Web Developer • Supporting and maintaining a bespoke motor accident management website • Creating add-ons and new services in an agile environment • Advisor to junior members of staff and introducer of new technologies to the team • Consulted with end users to achieve the best results -Inspired Gaming UK Ltd (Video Streaming and Back-end Services) Winter 2017 – Winter 2021 +Inspired Gaming UK Ltd (Video Streaming and Back-end Services) 2017 – 2021 Title: Senior Software Developer • Development ownership of solo-projects • Supporting and advising the development team @@ -63,14 +63,14 @@ Title: Senior Software Developer • Unit, Behaviour, and Integration testing using various suites • Completed multiple projects through to production -The Site Doctor (Small and Highly Skilled Software House) Winter 2021 – Winter 2023 +The Site Doctor (Small and Highly Skilled Software House) 2021 – 2024 Title: Software Developer • Full project ownership, requirement gathering, work specification, deployment and support • Supporting and advising the development team • Developed B2B APIs deployed to the Cloud using CI • Responsible for communicating with clients and work team -Self Employed (Material Product Arbitrage) Winter 2023 – Current +Self Employed (Material Product Arbitrage) 2023 - Onwards Title: N/A • Self-directed research and systems design • Responsibility for following regulatory requirements diff --git a/Rayson.CV/UI/src/data/cv.ts b/Rayson.CV/UI/src/data/cv.ts index bce9f4d..6429423 100644 --- a/Rayson.CV/UI/src/data/cv.ts +++ b/Rayson.CV/UI/src/data/cv.ts @@ -48,7 +48,7 @@ export const workExperience: WorkExperience[] = [ { id: 'self-employed', company: 'Self Employed (Material Product Arbitrage)', - period: 'Winter 2023 – Current', + period: '2023 - Onwards', yearStart: 2023, title: 'N/A', description: 'Self-directed research and systems design in material product arbitrage', @@ -61,7 +61,7 @@ export const workExperience: WorkExperience[] = [ { id: 'the-site-doctor', company: 'The Site Doctor (Small and Highly Skilled Software House)', - period: 'Winter 2021 – Winter 2023', + period: '2021 - 2024', yearStart: 2021, title: 'Software Developer', description: 'Full project ownership in a small, highly skilled software house', @@ -75,7 +75,7 @@ export const workExperience: WorkExperience[] = [ { id: 'inspired-gaming', company: 'Inspired Gaming UK Ltd (Video Streaming and Back-end Services)', - period: 'Winter 2017 – Winter 2021', + period: '2017 - 2021', yearStart: 2017, title: 'Senior Software Developer', description: 'Senior developer role in video streaming and back-end services', @@ -90,7 +90,7 @@ export const workExperience: WorkExperience[] = [ { id: 'arvs', company: 'ARVS ltd (Car Accident Claims Management)', - period: 'Summer 2014 – Summer 2016', + period: '2014 - 2016', yearStart: 2014, title: 'Web Developer', description: 'Web developer supporting a bespoke motor accident management website', @@ -102,9 +102,9 @@ export const workExperience: WorkExperience[] = [ ], }, { - id: 'dbl ogic', + id: 'dblogic', company: 'DBLogic (Software House)', - period: 'Spring 2012 – Spring 2014', + period: '2012 - 2014', yearStart: 2012, title: 'Software Development Engineer', description: 'Software development engineer in a software house environment', @@ -118,7 +118,7 @@ export const workExperience: WorkExperience[] = [ { id: 'freelance', company: 'Freelance (Personal and Brochure Websites)', - period: '2007 – 2012', + period: '2007 - 2012', yearStart: 2007, title: 'Website Development and Support', description: 'Freelance website development and support', diff --git a/Rayson.CV/UI/src/pages/TimelinePage.tsx b/Rayson.CV/UI/src/pages/TimelinePage.tsx index af1266f..95f1290 100644 --- a/Rayson.CV/UI/src/pages/TimelinePage.tsx +++ b/Rayson.CV/UI/src/pages/TimelinePage.tsx @@ -13,7 +13,7 @@ import { } from '../data/cv'; function getEndYear(period: string): number { - if (period.toLowerCase().includes('current')) return 2026; + if (period.toLowerCase().includes('current') || period.toLowerCase().includes('onwards')) return 2026; const match = period.match(/(\d{4})/g); if (!match) return 2026; return parseInt(match[match.length - 1]); From 56a6a9ff42460f28e5c4defc7b30b783798027ba Mon Sep 17 00:00:00 2001 From: Daniel William Frederick Rayson Date: Wed, 18 Mar 2026 13:41:04 +0000 Subject: [PATCH 2/8] renamed --- .../Ollama/ollama.docker-compose.yml | 2 +- .../Api/Infrastructure/ollama.Dockerfile | 14 - Rayson.CV/Api/Infrastructure/ollama.README.md | 265 ------------------ .../Infrastructure/ollama.docker-compose.yml | 27 -- .../Api/Infrastructure/ollama.entrypoint.sh | 45 --- .../Api/Infrastructure/ollama.healthcheck.sh | 29 -- Rayson.CV/docker-compose.dev.db-api.yml | 2 +- Rayson.CV/docker-compose.dev.db-ui.yml | 2 +- Rayson.CV/docker-compose.dev.db.yml | 2 +- Rayson.CV/docker-compose.dev.full.yml | 2 +- Rayson.CV/docker-compose.prod.full.yml | 2 +- 11 files changed, 6 insertions(+), 386 deletions(-) delete mode 100755 Rayson.CV/Api/Infrastructure/ollama.Dockerfile delete mode 100755 Rayson.CV/Api/Infrastructure/ollama.README.md delete mode 100755 Rayson.CV/Api/Infrastructure/ollama.docker-compose.yml delete mode 100755 Rayson.CV/Api/Infrastructure/ollama.entrypoint.sh delete mode 100755 Rayson.CV/Api/Infrastructure/ollama.healthcheck.sh diff --git a/Rayson.CV/Api/Infrastructure/Ollama/ollama.docker-compose.yml b/Rayson.CV/Api/Infrastructure/Ollama/ollama.docker-compose.yml index cb0690c..2bb9777 100755 --- a/Rayson.CV/Api/Infrastructure/Ollama/ollama.docker-compose.yml +++ b/Rayson.CV/Api/Infrastructure/Ollama/ollama.docker-compose.yml @@ -7,7 +7,7 @@ services: - CUSTOM_MODELS=["llama3.2:latest","nomic-embed-text"] volumes: - ollama_data:/root/.ollama - container_name: ollama + container_name: raysoncv-ollama healthcheck: test: ["CMD", "/ollama.healthcheck.sh"] interval: 10s diff --git a/Rayson.CV/Api/Infrastructure/ollama.Dockerfile b/Rayson.CV/Api/Infrastructure/ollama.Dockerfile deleted file mode 100755 index 1414c6a..0000000 --- a/Rayson.CV/Api/Infrastructure/ollama.Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM ollama/ollama:latest - -RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* - -ENV OLLAMA_HOST=0.0.0.0 - -COPY ollama.entrypoint.sh / -COPY ollama.healthcheck.sh / - -RUN chmod +x /ollama.entrypoint.sh /ollama.healthcheck.sh - -EXPOSE 11434 - -ENTRYPOINT ["/ollama.entrypoint.sh"] \ No newline at end of file diff --git a/Rayson.CV/Api/Infrastructure/ollama.README.md b/Rayson.CV/Api/Infrastructure/ollama.README.md deleted file mode 100755 index 52d6ca1..0000000 --- a/Rayson.CV/Api/Infrastructure/ollama.README.md +++ /dev/null @@ -1,265 +0,0 @@ -# Rayson.Ollama - -.NET client library for Ollama with Docker support. - -## Installation - -```bash -dotnet add package Rayson.Ollama -``` - -> **Note:** This package creates 5 files in your project when you build, these are your ollama configuration files. - -## QuickStart - -1. Install the NuGet package and Build your project -2. Move `ollama.Dockerfile` and `*.sh` scripts to suitable location -3. Merge `ollama.docker-compose.yml` into your compose -4. Update `ollama>build>context` to files location -5. Configure `CUSTOM_MODELS` with your desired models -6. Configure `Ollama__Url` for your deployment scenario (see Configuration>Docker section below) -7. Optionally add GPU support, see comment in compose file - -### Usage: - -Using .NET's ServiceCollection for dependency injection: - -```csharp -using Rayson.Ollama.Extensions -services.AddOllama(); -``` - -```csharp -using Rayson.Ollama -var ollama = sp.GetRequiredService(); - -// Generate text (non-streaming) -var result = await ollama.GenerateAsync("Hello", "smollm:135m"); -Console.WriteLine(result.Response); - -// Generate with streaming -await foreach (var chunk in ollama.GenerateStreamAsync("Hello", "smollm:135m")) -{ - Console.Write(chunk.Response); -} - -// Chat (non-streaming) -var chatResult = await ollama.ChatAsync( - [new() { Role = "user", Content = "Hello" }], - "smollm:135m"); -Console.WriteLine(chatResult.Message?.Content); - -// Chat with streaming -await foreach (var chunk in ollama.ChatStreamAsync( - [new() { Role = "user", Content = "Hello" }], - "smollm:135m")) -{ - Console.Write(chunk.Message?.Content); -} - -// Generate embeddings -var embedding = await ollama.EmbedAsync("Hello", "nomic-embed-text"); -Console.WriteLine(embedding.Embedding.Count); -``` - -## Prerequisites - -### NVIDIA GPU Support - -If using an NVIDIA GPU, install the [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html): - -```bash -# Configure the repository -curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \ - | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg -curl -fsSL https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \ - | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \ - | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list -sudo apt-get update - -# Install the toolkit -sudo apt-get install -y nvidia-container-toolkit - -# Configure Docker -sudo nvidia-ctk runtime configure --runtime=docker -sudo systemctl restart docker -``` - -After installation, enable NVIDIA GPU support in `ollama.docker-compose.yml` by uncommenting the `deploy` section. - -## Docker Setup - -### Running Ollama - -```bash -docker compose up --build -``` - -> **Note:** By default, Ollama models are persisted in a Docker volume (`ollama_data`). This preserves your downloaded models across container restarts. - -### Healthcheck - -The docker-compose includes a healthcheck that polls the Ollama API every 10 seconds. You can check the health status with: - -```bash -docker compose ps -``` - -### Using Multiple Models - -By default, the container starts with `smollm:135m` and `nomic-embed-text`. To configure models, edit the `CUSTOM_MODELS` environment variable in `ollama.docker-compose.yml`: - -```yaml -environment: - - CUSTOM_MODELS=["smollm:135m","nomic-embed-text"] -``` - -The `CUSTOM_MODELS` environment variable accepts a JSON array of model names. The entrypoint script will check if each model exists and pull it only if missing. - -## Configuration - -### Docker - -#### Ollama container - -| Environment Variable | Default | Description | -|---------------------|---------|-------------| -| `CUSTOM_MODELS` | `["smollm:135m","nomic-embed-text"]` | Models to check/pull on startup | - -> **Note:** The `OLLAMA_HOST` in the Ollama container is hardcoded to `0.0.0.0` so connections from other containers are accepted. This could raise security issues for your environment. - -#### Consumer container - -| Environment Variable | Default | Description | -|---------------------|---------|-------------| -| `Ollama__Url` | `http://ollama:11434` | URL for calling containers configuration to reach Ollama API (Docker internal network) | - -### Connecting to Ollama - -The `Ollama__Url` environment variable specifies the address other containers use to reach Ollama. Set this based on your deployment scenario: - -- **Container-to-container** (recommended): Use `http://ollama:11434` (Docker internal network) -- **Host machine access**: Use `http://localhost:11434` and add `network_mode: host` to your compose: - -```yaml -services: - your-app: - network_mode: host -``` - -### .NET Client - -Configure the Ollama URL in your .NET application to match the `Ollama__Url` value from your docker-compose: - -**Via docker-compose.yml:** -```yaml -services: - webapp: - build: . - image: my-example-app:latest - environment: - - Ollama__Url=http://ollama:11434 -``` - -The URL should match the `Ollama__Url` value in your docker-compose. - -## API Reference - -### IOllamaService - -```csharp -// Generate text (non-streaming) -Task GenerateAsync(string prompt, string model, GenerateOptions? options = null, CancellationToken ct = default) - -// Generate text with streaming -IAsyncEnumerable GenerateStreamAsync(string prompt, string model, GenerateOptions? options = null, CancellationToken ct = default) - -// Chat (non-streaming) -Task ChatAsync(IList messages, string model, ChatOptions? options = null, CancellationToken ct = default) - -// Chat with streaming -IAsyncEnumerable ChatStreamAsync(IList messages, string model, ChatOptions? options = null, CancellationToken ct = default) - -// Generate embeddings -Task EmbedAsync(string input, string model, CancellationToken ct = default) - -// List available models -Task> ListModelsAsync(CancellationToken ct = default) - -// Pull a model -Task PullModelAsync(string model, IProgress? progress = null, CancellationToken ct = default) - -// Delete a model -Task DeleteModelAsync(string model, CancellationToken ct = default) -``` - -### Options - -```csharp -// Options for Generate/GenerateStream -public class GenerateOptions -{ - public string? Model { get; set; } - public string? Prompt { get; set; } - public string? System { get; set; } - public string? Template { get; set; } - public string? Context { get; set; } - public bool Raw { get; set; } - public double? Temperature { get; set; } - public double? TopP { get; set; } - public int? TopK { get; set; } - public double? RepeatPenalty { get; set; } - public int? NumPredict { get; set; } - public IList? Stop { get; set; } -} - -// Options for Chat/ChatStream -public class ChatOptions -{ - public string? Model { get; set; } - public string? System { get; set; } - public IList? Messages { get; set; } - public double? Temperature { get; set; } - public double? TopP { get; set; } - public int? TopK { get; set; } - public double? RepeatPenalty { get; set; } - public int? NumPredict { get; set; } - public IList? Stop { get; set; } -} -``` - -### Response Types - -```csharp -public class OllamaMessage -{ - public string? Role { get; set; } - public string? Content { get; set; } -} - -public class OllamaResponse -{ - public string? Model { get; set; } - public bool Done { get; set; } - public string? DoneReason { get; set; } - public long? TotalDuration { get; set; } - public long? LoadDuration { get; set; } - public int? PromptEvalCount { get; set; } - public int? EvalCount { get; set; } - public long? EvalDuration { get; set; } -} - -public class GenerateResponse : OllamaResponse -{ - public string? Response { get; set; } -} - -public class ChatResponse : OllamaResponse -{ - public OllamaMessage? Message { get; set; } -} - -public record ModelInfo(string Name, string ModifiedAt, long Size); - -public record EmbeddingsResponse(IReadOnlyList Embedding); -``` diff --git a/Rayson.CV/Api/Infrastructure/ollama.docker-compose.yml b/Rayson.CV/Api/Infrastructure/ollama.docker-compose.yml deleted file mode 100755 index d5ae730..0000000 --- a/Rayson.CV/Api/Infrastructure/ollama.docker-compose.yml +++ /dev/null @@ -1,27 +0,0 @@ -services: - ollama: - build: - context: . - dockerfile: ollama.Dockerfile - environment: - - CUSTOM_MODELS=["smollm2:135m","nomic-embed-text"] - volumes: - - ollama_data:/root/.ollama - container_name: ollama - healthcheck: - test: ["CMD", "/ollama.healthcheck.sh"] - interval: 10s - timeout: 10s - retries: 30 - start_period: 10s - # NVIDIA GPU Support: Uncomment the following to enable NVIDIA GPU - # deploy: - # resources: - # reservations: - # devices: - # - driver: nvidia - # count: all - # capabilities: [gpu] - -volumes: - ollama_data: diff --git a/Rayson.CV/Api/Infrastructure/ollama.entrypoint.sh b/Rayson.CV/Api/Infrastructure/ollama.entrypoint.sh deleted file mode 100755 index 7c1928c..0000000 --- a/Rayson.CV/Api/Infrastructure/ollama.entrypoint.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash -set -e - -if [ -z "$OLLAMA_HOST" ]; then - echo "ERROR: OLLAMA_HOST environment variable is required" - exit 1 -fi - -if [ -z "$CUSTOM_MODELS" ]; then - echo "ERROR: CUSTOM_MODELS environment variable is required" - exit 1 -fi - -# This is done in the image build already -# echo "Installing curl..." -# apt-get update && apt-get install -y curl - -echo "Starting Ollama Service..." -ollama serve & - -echo "Waiting for Ollama to be ready..." -until curl -s http://localhost:11434/api/tags > /dev/null 2>&1; do - sleep 1 -done - -MODELS=${CUSTOM_MODELS} -MODELS_CLEANED=$(echo "$MODELS" | tr -d '[]"' | tr ',' '\n') - -for model in $MODELS_CLEANED; do - model=$(echo "$model" | tr -d '[:space:]') - if [ -z "$model" ]; then - continue - fi - - echo "Checking model: $model" - if ollama list | grep -q "$model"; then - echo "Model $model already exists" - else - echo "Pulling model: $model" - ollama pull "$model" - fi -done - -echo "Finished pulling models" -wait \ No newline at end of file diff --git a/Rayson.CV/Api/Infrastructure/ollama.healthcheck.sh b/Rayson.CV/Api/Infrastructure/ollama.healthcheck.sh deleted file mode 100755 index 23e83fa..0000000 --- a/Rayson.CV/Api/Infrastructure/ollama.healthcheck.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -set -x - -echo "CUSTOM_MODELS: $CUSTOM_MODELS" - -if ! curl -sf http://localhost:11434/api/tags > /dev/null 2>&1; then - echo "Healthcheck: Ollama API not responding" - exit 1 -fi - -RESPONSE=$(curl -s http://localhost:11434/api/tags) - -MODELS_CLEANED=$(echo "$CUSTOM_MODELS" | tr -d '[]"' | tr ',' '\n') -echo "Checking models: $MODELS_CLEANED" - -for model in $MODELS_CLEANED; do - model=$(echo "$model" | tr -d '[:space:]') - if [ -z "$model" ]; then - continue - fi - - if ! echo "$RESPONSE" | grep -q "$model"; then - echo "Healthcheck: Model $model not found" - exit 1 - fi -done - -echo "Healthcheck: All models ready" -exit 0 diff --git a/Rayson.CV/docker-compose.dev.db-api.yml b/Rayson.CV/docker-compose.dev.db-api.yml index a80bb84..da3a989 100644 --- a/Rayson.CV/docker-compose.dev.db-api.yml +++ b/Rayson.CV/docker-compose.dev.db-api.yml @@ -70,7 +70,7 @@ services: - CUSTOM_MODELS=["llama3.2:latest","nomic-embed-text"] volumes: - ollama_data:/root/.ollama - container_name: ollama + container_name: raysoncv-ollama healthcheck: test: ["CMD", "/ollama.healthcheck.sh"] interval: 10s diff --git a/Rayson.CV/docker-compose.dev.db-ui.yml b/Rayson.CV/docker-compose.dev.db-ui.yml index 34725aa..a991d8b 100644 --- a/Rayson.CV/docker-compose.dev.db-ui.yml +++ b/Rayson.CV/docker-compose.dev.db-ui.yml @@ -62,7 +62,7 @@ services: - CUSTOM_MODELS=["llama3.2:latest","nomic-embed-text"] volumes: - ollama_data:/root/.ollama - container_name: ollama + container_name: raysoncv-ollama healthcheck: test: ["CMD", "/ollama.healthcheck.sh"] interval: 10s diff --git a/Rayson.CV/docker-compose.dev.db.yml b/Rayson.CV/docker-compose.dev.db.yml index 39e1cf0..f292eb2 100644 --- a/Rayson.CV/docker-compose.dev.db.yml +++ b/Rayson.CV/docker-compose.dev.db.yml @@ -36,7 +36,7 @@ services: - CUSTOM_MODELS=["llama3.2:latest","nomic-embed-text"] volumes: - ollama_data:/root/.ollama - container_name: ollama + container_name: raysoncv-ollama healthcheck: test: ["CMD", "/ollama.healthcheck.sh"] interval: 10s diff --git a/Rayson.CV/docker-compose.dev.full.yml b/Rayson.CV/docker-compose.dev.full.yml index f55f02c..c3775f7 100644 --- a/Rayson.CV/docker-compose.dev.full.yml +++ b/Rayson.CV/docker-compose.dev.full.yml @@ -103,7 +103,7 @@ services: - CUSTOM_MODELS=["llama3.2:latest","nomic-embed-text"] volumes: - ollama_data:/root/.ollama - container_name: ollama + container_name: raysoncv-ollama healthcheck: test: ["CMD", "/ollama.healthcheck.sh"] interval: 10s diff --git a/Rayson.CV/docker-compose.prod.full.yml b/Rayson.CV/docker-compose.prod.full.yml index e173170..d4077a7 100644 --- a/Rayson.CV/docker-compose.prod.full.yml +++ b/Rayson.CV/docker-compose.prod.full.yml @@ -102,7 +102,7 @@ services: - CUSTOM_MODELS=["llama3.2:latest","nomic-embed-text"] volumes: - ollama_data:/root/.ollama - container_name: ollama + container_name: raysoncv-ollama healthcheck: test: ["CMD", "/ollama.healthcheck.sh"] interval: 30s From 4a2ad7ed373aa9235b42710fc3108809ec22b2f4 Mon Sep 17 00:00:00 2001 From: Daniel William Frederick Rayson Date: Wed, 18 Mar 2026 13:49:03 +0000 Subject: [PATCH 3/8] remove logger --- Rayson.CV/Api/Infrastructure/Chatbot/OllamaChatbotService.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Rayson.CV/Api/Infrastructure/Chatbot/OllamaChatbotService.cs b/Rayson.CV/Api/Infrastructure/Chatbot/OllamaChatbotService.cs index a51ecd1..4a92e94 100644 --- a/Rayson.CV/Api/Infrastructure/Chatbot/OllamaChatbotService.cs +++ b/Rayson.CV/Api/Infrastructure/Chatbot/OllamaChatbotService.cs @@ -7,8 +7,7 @@ namespace Infrastructure.Chatbot; public class OllamaChatbotService( IOllamaService ollamaService, - ICvProvider cvProvider, - ILogger logger) : IChatbotService + ICvProvider cvProvider) : IChatbotService { private const string Model = "llama3.2:latest"; private const int MaxTokens = 35; // ~100 words From 929381b2709c0d221cf972c22e999acf0eafff07 Mon Sep 17 00:00:00 2001 From: Daniel William Frederick Rayson Date: Wed, 18 Mar 2026 15:46:26 +0000 Subject: [PATCH 4/8] removed entire database, it's not used --- Rayson.CV/.env.production | 1 - Rayson.CV/Api/Database/Database.csproj | 24 -- .../DatabaseConfigurationException.cs | 6 - .../Extensions/ServiceCollectionExtensions.cs | 46 --- .../HealthChecks/NpgsqlHealthCheck.cs | 29 -- .../20260225083042_Init.Designer.cs | 303 ------------------ .../Migrations/20260225083042_Init.cs | 243 -------------- .../20260225093106_RemoveIdentity.Designer.cs | 29 -- .../20260225093106_RemoveIdentity.cs | 243 -------------- .../20260227205757_AddCvChunks.Designer.cs | 64 ---- .../Migrations/20260227205757_AddCvChunks.cs | 43 --- ...28000000_FixCvChunksVectorType.Designer.cs | 64 ---- .../20260228000000_FixCvChunksVectorType.cs | 49 --- .../20260317000000_ClearCvChunks.Designer.cs | 20 -- .../20260317000000_ClearCvChunks.cs | 21 -- .../RaysonCVDbContextModelSnapshot.cs | 61 ---- Rayson.CV/Api/Database/RaysonDevDbContext.cs | 36 --- Rayson.CV/Api/Database/Repository.cs | 93 ------ Rayson.CV/Api/Database/SeedData/DataSeeder.cs | 9 - .../Api/Database/SeedData/IDataSeeder.cs | 6 - Rayson.CV/Api/Dockerfile | 6 - Rayson.CV/Api/Domain/CvChunk.cs | 10 - Rayson.CV/Api/Domain/IRepository.cs | 17 - .../Extensions/ServiceCollectionExtensions.cs | 7 +- .../Health/OllamaHealthCheck.cs | 22 ++ .../Api/Infrastructure/Infrastructure.csproj | 1 - .../Infrastructure/RAG/CvChunkRepository.cs | 168 ---------- .../Infrastructure/RAG/ICvChunkRepository.cs | 9 - .../Infrastructure/RAG/IEmbeddingService.cs | 7 - .../Api/Infrastructure/RAG/IRagService.cs | 7 - .../RAG/OllamaEmbeddingService.cs | 41 --- .../Api/Infrastructure/RAG/RagService.cs | 260 --------------- .../Extensions/WebApplicationExtensions.cs | 25 -- .../Api/Presentation/Presentation.csproj | 5 - Rayson.CV/Api/Presentation/Program.cs | 5 - Rayson.CV/Api/RaysonCVBackend.sln | 6 - Rayson.CV/Api/prod.Dockerfile | 6 - Rayson.CV/docker-compose.dev.db-api.yml | 97 ------ Rayson.CV/docker-compose.dev.db.yml | 63 ---- Rayson.CV/docker-compose.dev.full.yml | 32 +- Rayson.CV/docker-compose.prod.full.yml | 34 +- 41 files changed, 28 insertions(+), 2190 deletions(-) delete mode 100644 Rayson.CV/Api/Database/Database.csproj delete mode 100644 Rayson.CV/Api/Database/Exceptions/DatabaseConfigurationException.cs delete mode 100644 Rayson.CV/Api/Database/Extensions/ServiceCollectionExtensions.cs delete mode 100644 Rayson.CV/Api/Database/HealthChecks/NpgsqlHealthCheck.cs delete mode 100644 Rayson.CV/Api/Database/Migrations/20260225083042_Init.Designer.cs delete mode 100644 Rayson.CV/Api/Database/Migrations/20260225083042_Init.cs delete mode 100644 Rayson.CV/Api/Database/Migrations/20260225093106_RemoveIdentity.Designer.cs delete mode 100644 Rayson.CV/Api/Database/Migrations/20260225093106_RemoveIdentity.cs delete mode 100644 Rayson.CV/Api/Database/Migrations/20260227205757_AddCvChunks.Designer.cs delete mode 100644 Rayson.CV/Api/Database/Migrations/20260227205757_AddCvChunks.cs delete mode 100644 Rayson.CV/Api/Database/Migrations/20260228000000_FixCvChunksVectorType.Designer.cs delete mode 100644 Rayson.CV/Api/Database/Migrations/20260228000000_FixCvChunksVectorType.cs delete mode 100644 Rayson.CV/Api/Database/Migrations/20260317000000_ClearCvChunks.Designer.cs delete mode 100644 Rayson.CV/Api/Database/Migrations/20260317000000_ClearCvChunks.cs delete mode 100644 Rayson.CV/Api/Database/Migrations/RaysonCVDbContextModelSnapshot.cs delete mode 100644 Rayson.CV/Api/Database/RaysonDevDbContext.cs delete mode 100644 Rayson.CV/Api/Database/Repository.cs delete mode 100644 Rayson.CV/Api/Database/SeedData/DataSeeder.cs delete mode 100644 Rayson.CV/Api/Database/SeedData/IDataSeeder.cs delete mode 100644 Rayson.CV/Api/Domain/CvChunk.cs delete mode 100644 Rayson.CV/Api/Domain/IRepository.cs create mode 100644 Rayson.CV/Api/Infrastructure/Health/OllamaHealthCheck.cs delete mode 100644 Rayson.CV/Api/Infrastructure/RAG/CvChunkRepository.cs delete mode 100644 Rayson.CV/Api/Infrastructure/RAG/ICvChunkRepository.cs delete mode 100644 Rayson.CV/Api/Infrastructure/RAG/IEmbeddingService.cs delete mode 100644 Rayson.CV/Api/Infrastructure/RAG/IRagService.cs delete mode 100644 Rayson.CV/Api/Infrastructure/RAG/OllamaEmbeddingService.cs delete mode 100644 Rayson.CV/Api/Infrastructure/RAG/RagService.cs delete mode 100644 Rayson.CV/Api/Presentation/Extensions/WebApplicationExtensions.cs delete mode 100644 Rayson.CV/docker-compose.dev.db-api.yml delete mode 100644 Rayson.CV/docker-compose.dev.db.yml diff --git a/Rayson.CV/.env.production b/Rayson.CV/.env.production index 41c0374..47988cd 100644 --- a/Rayson.CV/.env.production +++ b/Rayson.CV/.env.production @@ -1,3 +1,2 @@ -POSTGRES_PASSWORD=change_me_to_a_secure_password JWT_SIGNING_KEY=change_me_to_a_secure_jwt_signing_key GRAPH_CLIENT_SECRET=take_your_best_guess_lol diff --git a/Rayson.CV/Api/Database/Database.csproj b/Rayson.CV/Api/Database/Database.csproj deleted file mode 100644 index 465613d..0000000 --- a/Rayson.CV/Api/Database/Database.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - net8.0 - enable - enable - - - diff --git a/Rayson.CV/Api/Database/Exceptions/DatabaseConfigurationException.cs b/Rayson.CV/Api/Database/Exceptions/DatabaseConfigurationException.cs deleted file mode 100644 index 3e12f9e..0000000 --- a/Rayson.CV/Api/Database/Exceptions/DatabaseConfigurationException.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Database.Exceptions; - -public class DatabaseConfigurationException(string message) - : InvalidOperationException(message) -{ -} diff --git a/Rayson.CV/Api/Database/Extensions/ServiceCollectionExtensions.cs b/Rayson.CV/Api/Database/Extensions/ServiceCollectionExtensions.cs deleted file mode 100644 index bacf4d5..0000000 --- a/Rayson.CV/Api/Database/Extensions/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Database.Exceptions; -using Database.HealthChecks; -using Database.SeedData; -using Domain.Repositories; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace Database.Extensions; - -public static class ServiceCollectionExtensions -{ - public static void AddDatabaseServices(this IServiceCollection services, IConfiguration configuration) - { - var connectionString = BuildConnectionString(configuration); - - services.AddDbContext((options) => - { - options.UseNpgsql(connectionString); - }); - - services.AddScoped(typeof(IRepository<>), typeof(Repository<>)); - services.AddScoped(); - - services.AddHealthChecks() - .AddCheck("postgresql", tags: new[] { "ready" }); - } - - private static string BuildConnectionString(IConfiguration configuration) - { - var host = configuration["POSTGRES_HOST"]; - var port = configuration["POSTGRES_PORT"]; - var username = configuration["POSTGRES_USERNAME"]; - var password = configuration["POSTGRES_PASSWORD"]; - var database = configuration["POSTGRES_DATABASE"]; - - if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(port) || - string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password) || - string.IsNullOrEmpty(database)) - { - throw new DatabaseConfigurationException("Database connection string is missing. Please provide all PostgreSQL environment variables (POSTGRES_HOST, POSTGRES_PORT, POSTGRES_USERNAME, POSTGRES_PASSWORD, POSTGRES_DATABASE)."); - } - - return $"Server={host};Port={port};Database={database};User Id={username};Password={password};TrustServerCertificate=True"; - } -} diff --git a/Rayson.CV/Api/Database/HealthChecks/NpgsqlHealthCheck.cs b/Rayson.CV/Api/Database/HealthChecks/NpgsqlHealthCheck.cs deleted file mode 100644 index 223586e..0000000 --- a/Rayson.CV/Api/Database/HealthChecks/NpgsqlHealthCheck.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Diagnostics.HealthChecks; - -namespace Database.HealthChecks; - -public class NpgsqlHealthCheck : IHealthCheck -{ - private readonly RaysonCVDbContext _context; - - public NpgsqlHealthCheck(RaysonCVDbContext context) - { - _context = context; - } - - public async Task CheckHealthAsync( - HealthCheckContext context, - CancellationToken cancellationToken = default) - { - try - { - await _context.Database.ExecuteSqlRawAsync("SELECT 1", cancellationToken); - return HealthCheckResult.Healthy("PostgreSQL connection is healthy"); - } - catch (Exception ex) - { - return HealthCheckResult.Unhealthy("PostgreSQL connection failed", ex); - } - } -} diff --git a/Rayson.CV/Api/Database/Migrations/20260225083042_Init.Designer.cs b/Rayson.CV/Api/Database/Migrations/20260225083042_Init.Designer.cs deleted file mode 100644 index 2be7ccc..0000000 --- a/Rayson.CV/Api/Database/Migrations/20260225083042_Init.Designer.cs +++ /dev/null @@ -1,303 +0,0 @@ -// -using System; -using Database; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Database.Migrations -{ - [DbContext(typeof(RaysonCVDbContext))] - [Migration("20260225083042_Init")] - partial class Init - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.10") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Database.Auth.ApplicationRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("AspNetRoles", (string)null); - }); - - modelBuilder.Entity("Database.Auth.ApplicationUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AccessFailedCount") - .HasColumnType("integer"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("DisplayName") - .IsRequired() - .HasColumnType("text"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("boolean"); - - b.Property("LockoutEnabled") - .HasColumnType("boolean"); - - b.Property("LockoutEnd") - .HasColumnType("timestamp with time zone"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("PasswordHash") - .HasColumnType("text"); - - b.Property("PhoneNumber") - .HasColumnType("text"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("boolean"); - - b.Property("SecurityStamp") - .HasColumnType("text"); - - b.Property("TwoFactorEnabled") - .HasColumnType("boolean"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.ToTable("AspNetUsers", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("text"); - - b.Property("Xml") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("DataProtectionKeys"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("RoleId") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("ProviderKey") - .HasColumnType("text"); - - b.Property("ProviderDisplayName") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("integer"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("integer"); - - b.Property("RoleId") - .HasColumnType("integer"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("integer"); - - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Database.Auth.ApplicationRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Database.Auth.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Database.Auth.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Database.Auth.ApplicationRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Database.Auth.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Database.Auth.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Rayson.CV/Api/Database/Migrations/20260225083042_Init.cs b/Rayson.CV/Api/Database/Migrations/20260225083042_Init.cs deleted file mode 100644 index 25f8c51..0000000 --- a/Rayson.CV/Api/Database/Migrations/20260225083042_Init.cs +++ /dev/null @@ -1,243 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Database.Migrations -{ - /// - public partial class Init : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "AspNetRoles", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NormalizedName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetRoles", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AspNetUsers", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - DisplayName = table.Column(type: "text", nullable: false), - UserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NormalizedUserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - Email = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NormalizedEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - EmailConfirmed = table.Column(type: "boolean", nullable: false), - PasswordHash = table.Column(type: "text", nullable: true), - SecurityStamp = table.Column(type: "text", nullable: true), - ConcurrencyStamp = table.Column(type: "text", nullable: true), - PhoneNumber = table.Column(type: "text", nullable: true), - PhoneNumberConfirmed = table.Column(type: "boolean", nullable: false), - TwoFactorEnabled = table.Column(type: "boolean", nullable: false), - LockoutEnd = table.Column(type: "timestamp with time zone", nullable: true), - LockoutEnabled = table.Column(type: "boolean", nullable: false), - AccessFailedCount = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUsers", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "DataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - FriendlyName = table.Column(type: "text", nullable: true), - Xml = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_DataProtectionKeys", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AspNetRoleClaims", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - RoleId = table.Column(type: "integer", nullable: false), - ClaimType = table.Column(type: "text", nullable: true), - ClaimValue = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); - table.ForeignKey( - name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", - column: x => x.RoleId, - principalTable: "AspNetRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserClaims", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - UserId = table.Column(type: "integer", nullable: false), - ClaimType = table.Column(type: "text", nullable: true), - ClaimValue = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); - table.ForeignKey( - name: "FK_AspNetUserClaims_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserLogins", - columns: table => new - { - LoginProvider = table.Column(type: "text", nullable: false), - ProviderKey = table.Column(type: "text", nullable: false), - ProviderDisplayName = table.Column(type: "text", nullable: true), - UserId = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK_AspNetUserLogins_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserRoles", - columns: table => new - { - UserId = table.Column(type: "integer", nullable: false), - RoleId = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK_AspNetUserRoles_AspNetRoles_RoleId", - column: x => x.RoleId, - principalTable: "AspNetRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_AspNetUserRoles_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserTokens", - columns: table => new - { - UserId = table.Column(type: "integer", nullable: false), - LoginProvider = table.Column(type: "text", nullable: false), - Name = table.Column(type: "text", nullable: false), - Value = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK_AspNetUserTokens_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_AspNetRoleClaims_RoleId", - table: "AspNetRoleClaims", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "AspNetRoles", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserClaims_UserId", - table: "AspNetUserClaims", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserLogins_UserId", - table: "AspNetUserLogins", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserRoles_RoleId", - table: "AspNetUserRoles", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "AspNetUsers", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "AspNetUsers", - column: "NormalizedUserName", - unique: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "AspNetRoleClaims"); - - migrationBuilder.DropTable( - name: "AspNetUserClaims"); - - migrationBuilder.DropTable( - name: "AspNetUserLogins"); - - migrationBuilder.DropTable( - name: "AspNetUserRoles"); - - migrationBuilder.DropTable( - name: "AspNetUserTokens"); - - migrationBuilder.DropTable( - name: "DataProtectionKeys"); - - migrationBuilder.DropTable( - name: "AspNetRoles"); - - migrationBuilder.DropTable( - name: "AspNetUsers"); - } - } -} diff --git a/Rayson.CV/Api/Database/Migrations/20260225093106_RemoveIdentity.Designer.cs b/Rayson.CV/Api/Database/Migrations/20260225093106_RemoveIdentity.Designer.cs deleted file mode 100644 index 069a460..0000000 --- a/Rayson.CV/Api/Database/Migrations/20260225093106_RemoveIdentity.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -using Database; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Database.Migrations -{ - [DbContext(typeof(RaysonCVDbContext))] - [Migration("20260225093106_RemoveIdentity")] - partial class RemoveIdentity - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.10") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); -#pragma warning restore 612, 618 - } - } -} diff --git a/Rayson.CV/Api/Database/Migrations/20260225093106_RemoveIdentity.cs b/Rayson.CV/Api/Database/Migrations/20260225093106_RemoveIdentity.cs deleted file mode 100644 index f4f2d81..0000000 --- a/Rayson.CV/Api/Database/Migrations/20260225093106_RemoveIdentity.cs +++ /dev/null @@ -1,243 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Database.Migrations -{ - /// - public partial class RemoveIdentity : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "AspNetRoleClaims"); - - migrationBuilder.DropTable( - name: "AspNetUserClaims"); - - migrationBuilder.DropTable( - name: "AspNetUserLogins"); - - migrationBuilder.DropTable( - name: "AspNetUserRoles"); - - migrationBuilder.DropTable( - name: "AspNetUserTokens"); - - migrationBuilder.DropTable( - name: "DataProtectionKeys"); - - migrationBuilder.DropTable( - name: "AspNetRoles"); - - migrationBuilder.DropTable( - name: "AspNetUsers"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "AspNetRoles", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - ConcurrencyStamp = table.Column(type: "text", nullable: true), - Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NormalizedName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetRoles", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AspNetUsers", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - AccessFailedCount = table.Column(type: "integer", nullable: false), - ConcurrencyStamp = table.Column(type: "text", nullable: true), - DisplayName = table.Column(type: "text", nullable: false), - Email = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - EmailConfirmed = table.Column(type: "boolean", nullable: false), - LockoutEnabled = table.Column(type: "boolean", nullable: false), - LockoutEnd = table.Column(type: "timestamp with time zone", nullable: true), - NormalizedEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NormalizedUserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - PasswordHash = table.Column(type: "text", nullable: true), - PhoneNumber = table.Column(type: "text", nullable: true), - PhoneNumberConfirmed = table.Column(type: "boolean", nullable: false), - SecurityStamp = table.Column(type: "text", nullable: true), - TwoFactorEnabled = table.Column(type: "boolean", nullable: false), - UserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUsers", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "DataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - FriendlyName = table.Column(type: "text", nullable: true), - Xml = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_DataProtectionKeys", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AspNetRoleClaims", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - ClaimType = table.Column(type: "text", nullable: true), - ClaimValue = table.Column(type: "text", nullable: true), - RoleId = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); - table.ForeignKey( - name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", - column: x => x.RoleId, - principalTable: "AspNetRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserClaims", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - ClaimType = table.Column(type: "text", nullable: true), - ClaimValue = table.Column(type: "text", nullable: true), - UserId = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); - table.ForeignKey( - name: "FK_AspNetUserClaims_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserLogins", - columns: table => new - { - LoginProvider = table.Column(type: "text", nullable: false), - ProviderKey = table.Column(type: "text", nullable: false), - ProviderDisplayName = table.Column(type: "text", nullable: true), - UserId = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK_AspNetUserLogins_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserRoles", - columns: table => new - { - UserId = table.Column(type: "integer", nullable: false), - RoleId = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK_AspNetUserRoles_AspNetRoles_RoleId", - column: x => x.RoleId, - principalTable: "AspNetRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_AspNetUserRoles_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserTokens", - columns: table => new - { - UserId = table.Column(type: "integer", nullable: false), - LoginProvider = table.Column(type: "text", nullable: false), - Name = table.Column(type: "text", nullable: false), - Value = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK_AspNetUserTokens_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_AspNetRoleClaims_RoleId", - table: "AspNetRoleClaims", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "AspNetRoles", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserClaims_UserId", - table: "AspNetUserClaims", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserLogins_UserId", - table: "AspNetUserLogins", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserRoles_RoleId", - table: "AspNetUserRoles", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "AspNetUsers", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "AspNetUsers", - column: "NormalizedUserName", - unique: true); - } - } -} diff --git a/Rayson.CV/Api/Database/Migrations/20260227205757_AddCvChunks.Designer.cs b/Rayson.CV/Api/Database/Migrations/20260227205757_AddCvChunks.Designer.cs deleted file mode 100644 index f630e7d..0000000 --- a/Rayson.CV/Api/Database/Migrations/20260227205757_AddCvChunks.Designer.cs +++ /dev/null @@ -1,64 +0,0 @@ -// -using System; -using Database; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Database.Migrations -{ - [DbContext(typeof(RaysonCVDbContext))] - [Migration("20260227205757_AddCvChunks")] - partial class AddCvChunks - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.10") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Domain.CvChunk", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ChunkIndex") - .HasColumnType("integer"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("DeletedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Embedding") - .IsRequired() - .HasColumnType("double precision[]"); - - b.Property("Section") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("CvChunks"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Rayson.CV/Api/Database/Migrations/20260227205757_AddCvChunks.cs b/Rayson.CV/Api/Database/Migrations/20260227205757_AddCvChunks.cs deleted file mode 100644 index 941a8e3..0000000 --- a/Rayson.CV/Api/Database/Migrations/20260227205757_AddCvChunks.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Database.Migrations -{ - /// - public partial class AddCvChunks : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.Sql("CREATE EXTENSION IF NOT EXISTS vector"); - - migrationBuilder.CreateTable( - name: "CvChunks", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Content = table.Column(type: "text", nullable: false), - Section = table.Column(type: "text", nullable: false), - Embedding = table.Column(type: "double precision[]", nullable: false), - ChunkIndex = table.Column(type: "integer", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), - DeletedAt = table.Column(type: "timestamp with time zone", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_CvChunks", x => x.Id); - }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "CvChunks"); - } - } -} diff --git a/Rayson.CV/Api/Database/Migrations/20260228000000_FixCvChunksVectorType.Designer.cs b/Rayson.CV/Api/Database/Migrations/20260228000000_FixCvChunksVectorType.Designer.cs deleted file mode 100644 index be67098..0000000 --- a/Rayson.CV/Api/Database/Migrations/20260228000000_FixCvChunksVectorType.Designer.cs +++ /dev/null @@ -1,64 +0,0 @@ -// -using System; -using Database; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Database.Migrations -{ - [DbContext(typeof(RaysonCVDbContext))] - [Migration("20260228000000_FixCvChunksVectorType")] - partial class FixCvChunksVectorType - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.10") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Domain.CvChunk", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ChunkIndex") - .HasColumnType("integer"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("DeletedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Embedding") - .IsRequired() - .HasColumnType("vector(768)"); - - b.Property("Section") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("CvChunks"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Rayson.CV/Api/Database/Migrations/20260228000000_FixCvChunksVectorType.cs b/Rayson.CV/Api/Database/Migrations/20260228000000_FixCvChunksVectorType.cs deleted file mode 100644 index b3a619f..0000000 --- a/Rayson.CV/Api/Database/Migrations/20260228000000_FixCvChunksVectorType.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Database.Migrations -{ - /// - public partial class FixCvChunksVectorType : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.Sql("CREATE EXTENSION IF NOT EXISTS vector"); - - migrationBuilder.DropTable(name: "CvChunks"); - - migrationBuilder.CreateTable( - name: "CvChunks", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Content = table.Column(type: "text", nullable: false), - Section = table.Column(type: "text", nullable: false), - Embedding = table.Column(type: "vector(768)", nullable: false), - ChunkIndex = table.Column(type: "integer", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), - DeletedAt = table.Column(type: "timestamp with time zone", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_CvChunks", x => x.Id); - }); - - migrationBuilder.Sql(@" - CREATE INDEX IF NOT EXISTS cvchunks_embedding_idx - ON ""CvChunks"" - USING ivfflat (""Embedding"" vector_cosine_ops) - WITH (lists = 100)"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable(name: "CvChunks"); - } - } -} diff --git a/Rayson.CV/Api/Database/Migrations/20260317000000_ClearCvChunks.Designer.cs b/Rayson.CV/Api/Database/Migrations/20260317000000_ClearCvChunks.Designer.cs deleted file mode 100644 index a6e7e5e..0000000 --- a/Rayson.CV/Api/Database/Migrations/20260317000000_ClearCvChunks.Designer.cs +++ /dev/null @@ -1,20 +0,0 @@ -// -using Database; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Database.Migrations -{ - [DbContext(typeof(RaysonCVDbContext))] - [Migration("20260317000000_ClearCvChunks")] - partial class ClearCvChunks - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { - } - } -} diff --git a/Rayson.CV/Api/Database/Migrations/20260317000000_ClearCvChunks.cs b/Rayson.CV/Api/Database/Migrations/20260317000000_ClearCvChunks.cs deleted file mode 100644 index 26b505f..0000000 --- a/Rayson.CV/Api/Database/Migrations/20260317000000_ClearCvChunks.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Database.Migrations -{ - /// - public partial class ClearCvChunks : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.Sql("TRUNCATE TABLE \"CvChunks\" CASCADE"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - } - } -} diff --git a/Rayson.CV/Api/Database/Migrations/RaysonCVDbContextModelSnapshot.cs b/Rayson.CV/Api/Database/Migrations/RaysonCVDbContextModelSnapshot.cs deleted file mode 100644 index 7c92b30..0000000 --- a/Rayson.CV/Api/Database/Migrations/RaysonCVDbContextModelSnapshot.cs +++ /dev/null @@ -1,61 +0,0 @@ -// -using System; -using Database; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Database.Migrations -{ - [DbContext(typeof(RaysonCVDbContext))] - partial class RaysonCVDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.10") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Domain.CvChunk", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ChunkIndex") - .HasColumnType("integer"); - - b.Property("Content") - .IsRequired() - .HasColumnType("text"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("DeletedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Embedding") - .IsRequired() - .HasColumnType("vector(768)"); - - b.Property("Section") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("CvChunks"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Rayson.CV/Api/Database/RaysonDevDbContext.cs b/Rayson.CV/Api/Database/RaysonDevDbContext.cs deleted file mode 100644 index 4cbe25b..0000000 --- a/Rayson.CV/Api/Database/RaysonDevDbContext.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Globalization; -using Microsoft.EntityFrameworkCore; -using Domain; - -namespace Database; - -public class RaysonCVDbContext : DbContext -{ - public RaysonCVDbContext() - : base() { } - - public RaysonCVDbContext(DbContextOptions options) - : base(options) { } - - public DbSet CvChunks { get; set; } = null!; - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - optionsBuilder.UseNpgsql(); - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity(entity => - { - entity.Property(e => e.Embedding) - .HasColumnType("vector(768)") - .HasConversion( - v => "[" + string.Join(",", v.Select(e => e.ToString("G10", CultureInfo.InvariantCulture))) + "]", - v => v.Trim('[', ']').Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .Select(double.Parse) - .ToArray() - ); - }); - } -} \ No newline at end of file diff --git a/Rayson.CV/Api/Database/Repository.cs b/Rayson.CV/Api/Database/Repository.cs deleted file mode 100644 index 1f01465..0000000 --- a/Rayson.CV/Api/Database/Repository.cs +++ /dev/null @@ -1,93 +0,0 @@ -using Domain; -using Domain.Repositories; -using Microsoft.EntityFrameworkCore; - -namespace Database; - -public class Repository(RaysonCVDbContext context) : IRepository where T : Entity -{ - protected readonly DbContext _context = context; - protected readonly DbSet _dbSet = context.Set(); - - public IQueryable Query() - { - return _context.Set().AsQueryable(); - } - - public async Task AddAsync(T entity) - { - await _context.Set().AddAsync(entity); - await _context.SaveChangesAsync(); - } - - public async Task UpdateAsync(T entity) - { - _context.Entry(entity).State = EntityState.Modified; - await _context.SaveChangesAsync(); - } - - public async Task SoftDeleteAsync(T entity) - { - entity.DeletedAt = DateTime.UtcNow; - _context.Entry(entity).State = EntityState.Modified; - await _context.SaveChangesAsync(); - } - - public async Task HardDeleteAsync(T entity) - { - _context.Set().Remove(entity); - await _context.SaveChangesAsync(); - } - - public async Task> GetListAsync(IQueryable? predicate = null, bool includeDeleted = false) - { - IQueryable query = (predicate ?? Query()).Where(e => includeDeleted || e.DeletedAt == null); - return await query.ToListAsync(); - } - - public async Task GetByIdAsync(int id) - { - return await _context.Set().FindAsync(id); - } - - public async Task AddManyAsync(T[] entities) - { - foreach (var entity in entities) - { - await _context.Set().AddAsync(entity); - } - await _context.SaveChangesAsync(); - } - - public async Task UpdateManyAsync(T[] entities) - { - foreach (var entity in entities) - { - _context.Entry(entity).State = EntityState.Modified; - } - await _context.SaveChangesAsync(); - } - - public async Task SoftDeleteManyAsync(T[] entities) - { - foreach (var entity in entities) - { - entity.DeletedAt = DateTime.UtcNow; - _context.Entry(entity).State = EntityState.Modified; - } - await _context.SaveChangesAsync(); - } - - public async Task HardDeleteManyAsync(T[] entities) - { - foreach (var entity in entities) - { - _context.Set().Remove(entity); - } - await _context.SaveChangesAsync(); - } - public async Task> GetByIdManyAsync(int[] ids) - { - return await _context.Set().Where(e => ids.Contains(e.Id)).ToListAsync(); - } -} diff --git a/Rayson.CV/Api/Database/SeedData/DataSeeder.cs b/Rayson.CV/Api/Database/SeedData/DataSeeder.cs deleted file mode 100644 index fe14120..0000000 --- a/Rayson.CV/Api/Database/SeedData/DataSeeder.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Database.SeedData; - -public class DataSeeder : IDataSeeder -{ - public Task SeedDatabaseAsync() - { - return Task.CompletedTask; - } -} diff --git a/Rayson.CV/Api/Database/SeedData/IDataSeeder.cs b/Rayson.CV/Api/Database/SeedData/IDataSeeder.cs deleted file mode 100644 index 2ae1700..0000000 --- a/Rayson.CV/Api/Database/SeedData/IDataSeeder.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Database.SeedData; - -public interface IDataSeeder -{ - public Task SeedDatabaseAsync(); -} diff --git a/Rayson.CV/Api/Dockerfile b/Rayson.CV/Api/Dockerfile index aec5763..9663e31 100644 --- a/Rayson.CV/Api/Dockerfile +++ b/Rayson.CV/Api/Dockerfile @@ -13,7 +13,6 @@ COPY ["Presentation/Presentation.csproj", "Presentation/"] COPY ["Application/Application.csproj", "Application/"] COPY ["Domain/Domain.csproj", "Domain/"] COPY ["Infrastructure/Infrastructure.csproj", "Infrastructure/"] -COPY ["Database/Database.csproj", "Database/"] # Restore NuGet packages based on project references RUN dotnet restore "Presentation/Presentation.csproj" @@ -60,11 +59,6 @@ ENV ASPNETCORE_URLS=http://+:8080 ENV AuthOptions__Issuer=danrayson ENV AuthOptions__Audience=RaysonCVUsers ENV AuthOptions__IssuerSigningKey=development_signing_key_for_testing_only_not_for_production -ENV POSTGRES_HOST=host.docker.internal -ENV POSTGRES_PORT=5433 -ENV POSTGRES_DATABASE=raysoncv -ENV POSTGRES_USERNAME=postgres -ENV POSTGRES_PASSWORD=postgres ENV Cors__AllowedOrigins=http://localhost:3000 ENV OLLAMA__BASEURL=http://ollama:11434 diff --git a/Rayson.CV/Api/Domain/CvChunk.cs b/Rayson.CV/Api/Domain/CvChunk.cs deleted file mode 100644 index 4ad5b9e..0000000 --- a/Rayson.CV/Api/Domain/CvChunk.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Domain; - -public class CvChunk : Entity -{ - public required string Content { get; set; } - public required string Section { get; set; } - public required double[] Embedding { get; set; } - public int ChunkIndex { get; set; } - public DateTime CreatedAt { get; set; } = DateTime.UtcNow; -} diff --git a/Rayson.CV/Api/Domain/IRepository.cs b/Rayson.CV/Api/Domain/IRepository.cs deleted file mode 100644 index b9ce800..0000000 --- a/Rayson.CV/Api/Domain/IRepository.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Domain.Repositories; - -public interface IRepository where T : Entity -{ - IQueryable Query(); - Task AddAsync(T entity); - Task AddManyAsync(T[] entities); - Task UpdateAsync(T entity); - Task UpdateManyAsync(T[] entities); - Task SoftDeleteAsync(T entity); - Task SoftDeleteManyAsync(T[] entities); - Task HardDeleteAsync(T entity); - Task HardDeleteManyAsync(T[] entities); - Task> GetListAsync(IQueryable? predicate = null, bool includeDeleted = false); - Task GetByIdAsync(int id); - Task> GetByIdManyAsync(int[] ids); -} \ No newline at end of file diff --git a/Rayson.CV/Api/Infrastructure/Extensions/ServiceCollectionExtensions.cs b/Rayson.CV/Api/Infrastructure/Extensions/ServiceCollectionExtensions.cs index fb03899..16f82f2 100644 --- a/Rayson.CV/Api/Infrastructure/Extensions/ServiceCollectionExtensions.cs +++ b/Rayson.CV/Api/Infrastructure/Extensions/ServiceCollectionExtensions.cs @@ -6,9 +6,9 @@ using Infrastructure.Contact; using Infrastructure.Health; using Infrastructure.Logging; -using Infrastructure.RAG; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; using Rayson.Ollama.Extensions; namespace Infrastructure.Extensions; @@ -29,8 +29,7 @@ public static void AddInfrastructureServices(this IServiceCollection services, C services.AddOllama(); services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddHealthChecks() + .AddCheck("ollama", tags: new[] { "ready" }); } } diff --git a/Rayson.CV/Api/Infrastructure/Health/OllamaHealthCheck.cs b/Rayson.CV/Api/Infrastructure/Health/OllamaHealthCheck.cs new file mode 100644 index 0000000..b3b24c1 --- /dev/null +++ b/Rayson.CV/Api/Infrastructure/Health/OllamaHealthCheck.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Rayson.Ollama; + +namespace Infrastructure.Health; + +public class OllamaHealthCheck(IOllamaService ollamaService) : IHealthCheck +{ + public async Task CheckHealthAsync( + HealthCheckContext context, + CancellationToken cancellationToken = default) + { + try + { + var models = await ollamaService.ListModelsAsync(); + return HealthCheckResult.Healthy($"Ollama is running with {models.Count()} models available"); + } + catch (Exception ex) + { + return HealthCheckResult.Unhealthy("Ollama connection failed", ex); + } + } +} diff --git a/Rayson.CV/Api/Infrastructure/Infrastructure.csproj b/Rayson.CV/Api/Infrastructure/Infrastructure.csproj index 6773c8f..7e17a83 100644 --- a/Rayson.CV/Api/Infrastructure/Infrastructure.csproj +++ b/Rayson.CV/Api/Infrastructure/Infrastructure.csproj @@ -3,7 +3,6 @@ - diff --git a/Rayson.CV/Api/Infrastructure/RAG/CvChunkRepository.cs b/Rayson.CV/Api/Infrastructure/RAG/CvChunkRepository.cs deleted file mode 100644 index 95ac2f9..0000000 --- a/Rayson.CV/Api/Infrastructure/RAG/CvChunkRepository.cs +++ /dev/null @@ -1,168 +0,0 @@ -using Database; -using Microsoft.EntityFrameworkCore; -using Npgsql; - -namespace Infrastructure.RAG; - -public class CvChunkRepository(RaysonCVDbContext context) : ICvChunkRepository -{ - private readonly RaysonCVDbContext _context = context; - private const double SectionBoostAmount = 0.15; - - private static readonly Dictionary SectionKeywords = new(StringComparer.OrdinalIgnoreCase) - { - ["PERSONAL DETAILS"] = ["personal", "details", "contact", "email", "phone", "address", "name"], - ["EDUCATION"] = ["education", "school", "university", "degree", "qualification", "college", "gcse", "a-level", "mathematics"], - ["PROFESSIONAL SKILLS SUMMARY"] = ["skill", "skills", "tech", "technology", "stack", "experience", "expertise", "language", "framework", "tool"], - ["WORK EXPERIENCE"] = ["work", "job", "employment", "career", "position", "role", "company", "employer", "professional"], - ["HOME EXPERIENCE"] = ["home", "personal", "project", "projects", "hobby", "interest", "side"], - ["PERSONAL DESCRIPTION"] = ["about", "description", "profile", "background", "summary", "character"], - ["References"] = ["reference", "references", "referee", "contact"] - }; - - public async Task AddRangeAsync(IEnumerable chunks) - { - foreach (var chunk in chunks) - { - var embeddingStr = string.Join(",", chunk.Embedding.Select(e => e.ToString("G10", System.Globalization.CultureInfo.InvariantCulture))); - var deletedAtStr = chunk.DeletedAt.HasValue - ? $"'{chunk.DeletedAt.Value:u}'" - : "NULL"; - - await _context.Database.ExecuteSqlRawAsync($@" - INSERT INTO ""CvChunks"" (""Content"", ""Section"", ""Embedding"", ""ChunkIndex"", ""CreatedAt"", ""DeletedAt"") - VALUES ('{chunk.Content.Replace("'", "''")}', '{chunk.Section.Replace("'", "''")}', '[{embeddingStr}]'::vector, {chunk.ChunkIndex}, '{chunk.CreatedAt:u}', {deletedAtStr})"); - } - } - - public async Task ExistsAsync() - { - return await _context.CvChunks.AnyAsync(); - } - - public async Task> GetMostSimilarAsync(float[] embedding, int topK) - { - return await GetMostSimilarAsync(embedding, topK, null); - } - - public async Task> GetMostSimilarAsync(float[] embedding, int topK, string? query) - { - var embeddingString = string.Join(",", embedding.Select(e => e.ToString("G10", System.Globalization.CultureInfo.InvariantCulture))); - - var (matchedSection, boost) = query != null - ? FindMatchingSection(query) - : (null, 0.0); - - string sql; - if (matchedSection != null && boost > 0) - { - sql = $@" - SELECT ""Content"" - FROM ""CvChunks"" - ORDER BY ""Embedding"" <-> '[{embeddingString}]'::vector + - (CASE WHEN ""Section"" = '{matchedSection.Replace("'", "''")}' THEN {boost.ToString("G10", System.Globalization.CultureInfo.InvariantCulture)} ELSE 0 END) - LIMIT {topK}"; - } - else - { - sql = $@" - SELECT ""Content"" - FROM ""CvChunks"" - ORDER BY ""Embedding"" <-> '[{embeddingString}]'::vector - LIMIT {topK}"; - } - - var result = await _context.Database - .SqlQueryRaw(sql) - .ToListAsync(); - - return result; - } - - private static (string? Section, double Boost) FindMatchingSection(string query) - { - var queryLower = query.ToLowerInvariant(); - var queryWords = queryLower.Split([' ', ',', '.', '!', '?', ';', ':', '\t', '\n', '\r'], StringSplitOptions.RemoveEmptyEntries); - - if (queryWords.Length == 0) - return (null, 0); - - var sectionScores = new Dictionary(StringComparer.OrdinalIgnoreCase); - - foreach (var (section, keywords) in SectionKeywords) - { - var score = 0.0; - - foreach (var queryWord in queryWords) - { - if (string.IsNullOrWhiteSpace(queryWord) || queryWord.Length < 3) - continue; - - foreach (var keyword in keywords) - { - var similarity = CalculateSimilarity(queryWord, keyword); - if (similarity > 0.6) - { - score += similarity; - } - else if (keyword.Contains(queryWord, StringComparison.OrdinalIgnoreCase) || - queryWord.Contains(keyword, StringComparison.OrdinalIgnoreCase)) - { - score += 0.5; - } - } - } - - if (score > 0) - { - sectionScores[section] = score; - } - } - - if (sectionScores.Count == 0) - return (null, 0); - - var bestSection = sectionScores.OrderByDescending(x => x.Value).First(); - - var maxPossibleScore = queryWords.Length * 1.0; - var normalizedBoost = Math.Min(bestSection.Value / maxPossibleScore, 1.0) * SectionBoostAmount; - - return (bestSection.Key, normalizedBoost); - } - - private static double CalculateSimilarity(string s1, string s2) - { - if (string.IsNullOrEmpty(s1) || string.IsNullOrEmpty(s2)) - return 0; - - var distance = LevenshteinDistance(s1, s2); - var maxLength = Math.Max(s1.Length, s2.Length); - - return 1.0 - (double)distance / maxLength; - } - - private static int LevenshteinDistance(string s1, string s2) - { - var m = s1.Length; - var n = s2.Length; - var d = new int[m + 1, n + 1]; - - for (var i = 0; i <= m; i++) - d[i, 0] = i; - for (var j = 0; j <= n; j++) - d[0, j] = j; - - for (var i = 1; i <= m; i++) - { - for (var j = 1; j <= n; j++) - { - var cost = s1[i - 1] == s2[j - 1] ? 0 : 1; - d[i, j] = Math.Min( - Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), - d[i - 1, j - 1] + cost); - } - } - - return d[m, n]; - } -} diff --git a/Rayson.CV/Api/Infrastructure/RAG/ICvChunkRepository.cs b/Rayson.CV/Api/Infrastructure/RAG/ICvChunkRepository.cs deleted file mode 100644 index 77ef9bd..0000000 --- a/Rayson.CV/Api/Infrastructure/RAG/ICvChunkRepository.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Infrastructure.RAG; - -public interface ICvChunkRepository -{ - Task AddRangeAsync(IEnumerable chunks); - Task ExistsAsync(); - Task> GetMostSimilarAsync(float[] embedding, int topK); - Task> GetMostSimilarAsync(float[] embedding, int topK, string? query); -} diff --git a/Rayson.CV/Api/Infrastructure/RAG/IEmbeddingService.cs b/Rayson.CV/Api/Infrastructure/RAG/IEmbeddingService.cs deleted file mode 100644 index 9aed865..0000000 --- a/Rayson.CV/Api/Infrastructure/RAG/IEmbeddingService.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Infrastructure.RAG; - -public interface IEmbeddingService -{ - Task GenerateEmbeddingAsync(string text); - Task EnsureModelAvailableAsync(); -} diff --git a/Rayson.CV/Api/Infrastructure/RAG/IRagService.cs b/Rayson.CV/Api/Infrastructure/RAG/IRagService.cs deleted file mode 100644 index 7a5c97d..0000000 --- a/Rayson.CV/Api/Infrastructure/RAG/IRagService.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Infrastructure.RAG; - -public interface IRagService -{ - Task InitializeAsync(); - Task> SearchAsync(string query, int topK = 3); -} diff --git a/Rayson.CV/Api/Infrastructure/RAG/OllamaEmbeddingService.cs b/Rayson.CV/Api/Infrastructure/RAG/OllamaEmbeddingService.cs deleted file mode 100644 index f394681..0000000 --- a/Rayson.CV/Api/Infrastructure/RAG/OllamaEmbeddingService.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Microsoft.Extensions.Logging; -using Rayson.Ollama; - -namespace Infrastructure.RAG; - -public class OllamaEmbeddingService( - IOllamaService ollamaService, - ILogger logger) : IEmbeddingService -{ - private const string EmbeddingModel = "nomic-embed-text:latest"; - - public async Task GenerateEmbeddingAsync(string text) - { - var response = await ollamaService.EmbedAsync(text, EmbeddingModel); - - if (response?.Embedding == null) - { - throw new Exception("Invalid response from Ollama embedding endpoint"); - } - - return response.Embedding.Select(x => (float)x).ToArray(); - } - - public async Task EnsureModelAvailableAsync() - { - logger.LogInformation("Checking if embedding model {Model} is available", EmbeddingModel); - - var models = await ollamaService.ListModelsAsync(); - var modelExists = models.Any(m => m.Name == EmbeddingModel); - - if (!modelExists) - { - logger.LogInformation("Pulling embedding model {Model}...", EmbeddingModel); - await ollamaService.PullModelAsync(EmbeddingModel); - } - else - { - logger.LogInformation("Embedding model {Model} is already available", EmbeddingModel); - } - } -} diff --git a/Rayson.CV/Api/Infrastructure/RAG/RagService.cs b/Rayson.CV/Api/Infrastructure/RAG/RagService.cs deleted file mode 100644 index 22497bc..0000000 --- a/Rayson.CV/Api/Infrastructure/RAG/RagService.cs +++ /dev/null @@ -1,260 +0,0 @@ -using Application.Chatbot; -using Domain; -using Microsoft.Extensions.Logging; - -namespace Infrastructure.RAG; - -public class RagService( - ICvChunkRepository cvChunkRepository, - IEmbeddingService embeddingService, - ICvProvider cvProvider, - ILogger logger) : IRagService -{ - private const int MaxChunkSize = 350; - private const int MinChunkSize = 100; - - public async Task InitializeAsync() - { - if (await cvChunkRepository.ExistsAsync()) - { - logger.LogInformation("CV chunks already exist in database, skipping RAG initialization"); - return; - } - - logger.LogInformation("Starting RAG initialization..."); - - await embeddingService.EnsureModelAvailableAsync(); - - var cvContent = cvProvider.GetCvContent(); - var chunks = ChunkCvContent(cvContent); - - logger.LogInformation("Generated {Count} chunks from CV", chunks.Count); - - var cvChunks = new List(); - foreach (var (content, section, index) in chunks) - { - logger.LogDebug("Generating embedding for chunk {Index} ({Section})", index, section); - var embedding = await embeddingService.GenerateEmbeddingAsync(content); - - cvChunks.Add(new CvChunk - { - Content = content, - Section = section, - Embedding = [.. embedding.Select(e => (double)e)], - ChunkIndex = index, - CreatedAt = DateTime.UtcNow - }); - } - - await cvChunkRepository.AddRangeAsync(cvChunks); - - logger.LogInformation("RAG initialization complete. Saved {Count} chunks to database", cvChunks.Count); - } - - public async Task> SearchAsync(string query, int topK = 3) - { - var queryEmbedding = await embeddingService.GenerateEmbeddingAsync(query); - var results = await cvChunkRepository.GetMostSimilarAsync(queryEmbedding, topK, query); - - return results; - } - - private static List<(string Content, string Section, int Index)> ChunkCvContent(string cvContent) - { - var chunks = new List<(string Content, string Section, int Index)>(); - var lines = cvContent.Split('\n'); - var currentSection = "General"; - var sectionContent = new List(); - var chunkIndex = 0; - - var sectionHeaders = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "PERSONAL DETAILS", - "EDUCATION", - "PROFESSIONAL SKILLS SUMMARY", - "WORK EXPERIENCE", - "HOME EXPERIENCE", - "PERSONAL DESCRIPTION", - "References" - }; - - foreach (var line in lines) - { - var trimmedLine = line.Trim(); - - if (sectionHeaders.Contains(trimmedLine)) - { - if (sectionContent.Count > 0) - { - var sectionText = string.Join("\n", sectionContent); - var sectionChunks = SplitIntoChunks(sectionText, currentSection); - chunks.AddRange(sectionChunks.Select((content, i) => (content, currentSection, chunkIndex++))); - sectionContent.Clear(); - } - - currentSection = trimmedLine; - } - else - { - if(!string.IsNullOrWhiteSpace(line)) - sectionContent.Add(line); - } - } - - if (sectionContent.Count > 0) - { - var sectionText = string.Join("\n", sectionContent); - var sectionChunks = SplitIntoChunks(sectionText, currentSection); - chunks.AddRange(sectionChunks.Select((content, i) => (content, currentSection, chunkIndex++))); - } - - return chunks; - } - - private static List SplitIntoChunks(string text, string section) - { - if (string.IsNullOrWhiteSpace(text)) - return []; - - var chunks = new List(); - var paragraphs = text.Split("\n\n", StringSplitOptions.RemoveEmptyEntries); - - foreach (var paragraph in paragraphs) - { - var trimmedParagraph = paragraph.Trim(); - if (string.IsNullOrWhiteSpace(trimmedParagraph)) - continue; - - if (trimmedParagraph.Length <= MaxChunkSize) - { - chunks.Add(trimmedParagraph); - continue; - } - - var lines = trimmedParagraph.Split('\n', StringSplitOptions.RemoveEmptyEntries); - var currentChunk = new List(); - - foreach (var line in lines) - { - var trimmedLine = line.Trim(); - if (string.IsNullOrWhiteSpace(trimmedLine)) - continue; - - var potentialChunk = currentChunk.Count > 0 - ? string.Join("\n", currentChunk) + "\n" + trimmedLine - : trimmedLine; - - if (potentialChunk.Length <= MaxChunkSize) - { - currentChunk.Add(trimmedLine); - } - else - { - if (currentChunk.Count > 0) - { - chunks.Add(string.Join("\n", currentChunk)); - } - - if (trimmedLine.Length > MaxChunkSize) - { - chunks.AddRange(SplitBySentences(trimmedLine)); - currentChunk.Clear(); - } - else - { - currentChunk = [trimmedLine]; - } - } - } - - if (currentChunk.Count > 0) - { - chunks.Add(string.Join("\n", currentChunk)); - } - } - - return chunks; - } - - private static List SplitBySentences(string text) - { - var chunks = new List(); - if (string.IsNullOrWhiteSpace(text)) - return chunks; - - var sentences = SplitIntoSentences(text); - var currentChunk = new List(); - - foreach (var sentence in sentences) - { - var trimmed = sentence.Trim(); - if (string.IsNullOrWhiteSpace(trimmed)) - continue; - - var potentialChunk = currentChunk.Count > 0 - ? string.Join(" ", currentChunk) + " " + trimmed - : trimmed; - - if (potentialChunk.Length <= MaxChunkSize) - { - currentChunk.Add(trimmed); - } - else - { - if (currentChunk.Count > 0) - { - chunks.Add(string.Join(" ", currentChunk)); - } - - if (trimmed.Length > MaxChunkSize) - { - for (var i = 0; i < trimmed.Length; i += MaxChunkSize - MinChunkSize) - { - var chunk = trimmed.Substring(i, Math.Min(MaxChunkSize - MinChunkSize, trimmed.Length - i)); - if (!string.IsNullOrWhiteSpace(chunk)) - chunks.Add(chunk); - } - currentChunk.Clear(); - } - else - { - currentChunk = [trimmed]; - } - } - } - - if (currentChunk.Count > 0) - { - chunks.Add(string.Join(" ", currentChunk)); - } - - return chunks; - } - - private static string[] SplitIntoSentences(string text) - { - var sentences = new List(); - var current = new System.Text.StringBuilder(); - - foreach (var c in text) - { - current.Append(c); - - if (c == '.' || c == '!' || c == '?') - { - if (current.Length > 0) - { - sentences.Add(current.ToString()); - current.Clear(); - } - } - } - - if (current.Length > 0) - { - sentences.Add(current.ToString()); - } - - return sentences.ToArray(); - } -} diff --git a/Rayson.CV/Api/Presentation/Extensions/WebApplicationExtensions.cs b/Rayson.CV/Api/Presentation/Extensions/WebApplicationExtensions.cs deleted file mode 100644 index 7d3b59f..0000000 --- a/Rayson.CV/Api/Presentation/Extensions/WebApplicationExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Database; -using Database.SeedData; -using Infrastructure.RAG; -using Microsoft.EntityFrameworkCore; - -namespace Presentation.Extensions; - -public static class WebApplicationExtensions -{ - public static async Task RunMigrationsAsync(this WebApplication webApplication) - { - var scope = webApplication.Services.CreateScope(); - var context = scope.ServiceProvider.GetRequiredService(); - await context.Database.MigrateAsync(); - var seeder = scope.ServiceProvider.GetRequiredService(); - await seeder.SeedDatabaseAsync(); - } - - public static async Task InitializeRagAsync(this WebApplication webApplication) - { - var scope = webApplication.Services.CreateScope(); - var ragService = scope.ServiceProvider.GetRequiredService(); - await ragService.InitializeAsync(); - } -} diff --git a/Rayson.CV/Api/Presentation/Presentation.csproj b/Rayson.CV/Api/Presentation/Presentation.csproj index d7a9e2a..f7d6372 100644 --- a/Rayson.CV/Api/Presentation/Presentation.csproj +++ b/Rayson.CV/Api/Presentation/Presentation.csproj @@ -8,16 +8,11 @@ - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - diff --git a/Rayson.CV/Api/Presentation/Program.cs b/Rayson.CV/Api/Presentation/Program.cs index 06663af..c8743be 100644 --- a/Rayson.CV/Api/Presentation/Program.cs +++ b/Rayson.CV/Api/Presentation/Program.cs @@ -1,6 +1,4 @@ using Presentation.Extensions; -using Database.SeedData; -using Database.Extensions; using Presentation.Endpoints.Health; using Presentation.Endpoints.Logging; using Presentation.Endpoints.Chatbot; @@ -40,7 +38,6 @@ }); builder.Services.AddPresentationServices(); - builder.Services.AddDatabaseServices(builder.Configuration); builder.Services.AddInfrastructureServices(builder.Configuration); var app = builder.Build(); @@ -72,8 +69,6 @@ }); } - await app.RunMigrationsAsync(); - await app.InitializeRagAsync(); app.Run(); } catch (Exception ex) diff --git a/Rayson.CV/Api/RaysonCVBackend.sln b/Rayson.CV/Api/RaysonCVBackend.sln index b9f42f2..1c4ffcb 100644 --- a/Rayson.CV/Api/RaysonCVBackend.sln +++ b/Rayson.CV/Api/RaysonCVBackend.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Domain", "Domain\Domain.csproj", "{233DCA60-C404-4534-A551-0263A2308E08}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Database", "Database\Database.csproj", "{B6C6ED32-816C-4BF9-8A64-6148EC9E13B8}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Application", "Application\Application.csproj", "{1534280A-186B-4B41-A481-DC5A51D6D3A2}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "Infrastructure\Infrastructure.csproj", "{146B7D4B-6453-4CB8-A15A-EFF007B603DA}" @@ -23,10 +21,6 @@ Global {233DCA60-C404-4534-A551-0263A2308E08}.Debug|Any CPU.Build.0 = Debug|Any CPU {233DCA60-C404-4534-A551-0263A2308E08}.Release|Any CPU.ActiveCfg = Release|Any CPU {233DCA60-C404-4534-A551-0263A2308E08}.Release|Any CPU.Build.0 = Release|Any CPU - {B6C6ED32-816C-4BF9-8A64-6148EC9E13B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B6C6ED32-816C-4BF9-8A64-6148EC9E13B8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B6C6ED32-816C-4BF9-8A64-6148EC9E13B8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B6C6ED32-816C-4BF9-8A64-6148EC9E13B8}.Release|Any CPU.Build.0 = Release|Any CPU {1534280A-186B-4B41-A481-DC5A51D6D3A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1534280A-186B-4B41-A481-DC5A51D6D3A2}.Debug|Any CPU.Build.0 = Debug|Any CPU {1534280A-186B-4B41-A481-DC5A51D6D3A2}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/Rayson.CV/Api/prod.Dockerfile b/Rayson.CV/Api/prod.Dockerfile index 4118173..96ee784 100644 --- a/Rayson.CV/Api/prod.Dockerfile +++ b/Rayson.CV/Api/prod.Dockerfile @@ -13,7 +13,6 @@ COPY ["Presentation/Presentation.csproj", "Presentation/"] COPY ["Application/Application.csproj", "Application/"] COPY ["Domain/Domain.csproj", "Domain/"] COPY ["Infrastructure/Infrastructure.csproj", "Infrastructure/"] -COPY ["Database/Database.csproj", "Database/"] # Restore NuGet packages based on project references RUN dotnet restore "Presentation/Presentation.csproj" @@ -64,11 +63,6 @@ ENV ASPNETCORE_URLS=http://+:8080 # via docker-compose or environment file: # # Required: -# - POSTGRES_HOST (e.g., postgres) -# - POSTGRES_PORT (e.g., 5432) -# - POSTGRES_DATABASE (e.g., raysoncv) -# - POSTGRES_USERNAME (e.g., postgres) -# - POSTGRES_PASSWORD (secure password) # - OLLAMA__URL (e.g., http://ollama:11434) # # Optional: diff --git a/Rayson.CV/docker-compose.dev.db-api.yml b/Rayson.CV/docker-compose.dev.db-api.yml deleted file mode 100644 index a80bb84..0000000 --- a/Rayson.CV/docker-compose.dev.db-api.yml +++ /dev/null @@ -1,97 +0,0 @@ -# =========================================== -# Docker Compose - Development: Database + API -# =========================================== -# Usage: docker compose -f docker-compose.dev.db-api.yml up -d -# Purpose: Run PostgreSQL and API in Docker, debug UI locally -# UI connects to API at http://localhost:13245 -# =========================================== - -services: - postgres: - image: pgvector/pgvector:pg16 - container_name: raysoncv-postgres - restart: unless-stopped - environment: - POSTGRES_DB: raysoncv - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - volumes: - - postgres-data:/var/lib/postgresql/data - networks: - - internal - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres -d raysoncv"] - interval: 10s - timeout: 5s - retries: 5 - start_period: 10s - - api: - build: - context: ./Api - dockerfile: Dockerfile - container_name: raysoncv-api - restart: unless-stopped - environment: - POSTGRES_HOST: postgres - POSTGRES_PORT: 5432 - POSTGRES_DATABASE: raysoncv - POSTGRES_USERNAME: postgres - POSTGRES_PASSWORD: postgres - ASPNETCORE_ENVIRONMENT: Development - Graph__ClientSecret: notachancebuddy - Cors__AllowedOrigins: http://localhost:3000,http://localhost:5173 - LOG_LEVEL: Debug - Ollama__Url: http://ollama:11434 - ports: - - "13245:8080" - depends_on: - postgres: - condition: service_healthy - ollama: - condition: service_healthy - networks: - - internal - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8080/health/live"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - - # =========================================== - # Ollama (AI Chatbot) - # =========================================== - ollama: - build: - context: ./Api/Infrastructure/Ollama - dockerfile: ollama.Dockerfile - environment: - - CUSTOM_MODELS=["llama3.2:latest","nomic-embed-text"] - volumes: - - ollama_data:/root/.ollama - container_name: ollama - healthcheck: - test: ["CMD", "/ollama.healthcheck.sh"] - interval: 10s - timeout: 10s - retries: 30 - start_period: 10s - networks: - - internal - # NVIDIA GPU Support: Uncomment the following to enable NVIDIA GPU - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: all - capabilities: [gpu] - -networks: - internal: - driver: bridge - -volumes: - postgres-data: - ollama_data: diff --git a/Rayson.CV/docker-compose.dev.db.yml b/Rayson.CV/docker-compose.dev.db.yml deleted file mode 100644 index 39e1cf0..0000000 --- a/Rayson.CV/docker-compose.dev.db.yml +++ /dev/null @@ -1,63 +0,0 @@ -# =========================================== -# Docker Compose - Development: Database Only -# =========================================== -# Usage: docker compose -f docker-compose.dev.db.yml up -d -# Purpose: Run only PostgreSQL for local debugging of API and UI -# =========================================== - -services: - postgres: - image: pgvector/pgvector:pg16 - container_name: raysoncv-postgres - restart: unless-stopped - environment: - POSTGRES_DB: raysoncv - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - volumes: - - postgres-data:/var/lib/postgresql/data - ports: - - "5433:5432" - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres -d raysoncv"] - interval: 10s - timeout: 5s - retries: 5 - start_period: 10s - - # =========================================== - # Ollama (AI Chatbot) - # =========================================== - ollama: - build: - context: ./Api/Infrastructure/Ollama - dockerfile: ollama.Dockerfile - environment: - - CUSTOM_MODELS=["llama3.2:latest","nomic-embed-text"] - volumes: - - ollama_data:/root/.ollama - container_name: ollama - healthcheck: - test: ["CMD", "/ollama.healthcheck.sh"] - interval: 10s - timeout: 10s - retries: 30 - start_period: 10s - networks: - - internal - # NVIDIA GPU Support: Uncomment the following to enable NVIDIA GPU - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: all - capabilities: [gpu] - -volumes: - postgres-data: - ollama_data: - -networks: - internal: - driver: bridge diff --git a/Rayson.CV/docker-compose.dev.full.yml b/Rayson.CV/docker-compose.dev.full.yml index f55f02c..0f4afdf 100644 --- a/Rayson.CV/docker-compose.dev.full.yml +++ b/Rayson.CV/docker-compose.dev.full.yml @@ -2,32 +2,10 @@ # Docker Compose - Development: Full Stack # =========================================== # Usage: docker compose -f docker-compose.dev.full.yml up -d -# Purpose: Run all services (PostgreSQL, API, UI) in Docker +# Purpose: Run all services (API, UI, Ollama) in Docker # =========================================== services: - # =========================================== - # PostgreSQL Database - # =========================================== - postgres: - image: pgvector/pgvector:pg16 - container_name: raysoncv-postgres - restart: unless-stopped - environment: - POSTGRES_DB: raysoncv - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - volumes: - - postgres-data:/var/lib/postgresql/data - networks: - - internal - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres -d raysoncv"] - interval: 10s - timeout: 5s - retries: 5 - start_period: 10s - # =========================================== # API (.NET 8.0) # =========================================== @@ -38,11 +16,6 @@ services: container_name: raysoncv-api restart: unless-stopped environment: - POSTGRES_HOST: postgres - POSTGRES_PORT: 5432 - POSTGRES_DATABASE: raysoncv - POSTGRES_USERNAME: postgres - POSTGRES_PASSWORD: postgres ASPNETCORE_ENVIRONMENT: Development Graph__ClientSecret: fake-passwrd Cors__AllowedOrigins: http://localhost:3000 @@ -51,8 +24,6 @@ services: ports: - "13245:8080" depends_on: - postgres: - condition: service_healthy ollama: condition: service_healthy networks: @@ -132,5 +103,4 @@ networks: # Volumes # =========================================== volumes: - postgres-data: ollama_data: diff --git a/Rayson.CV/docker-compose.prod.full.yml b/Rayson.CV/docker-compose.prod.full.yml index e173170..259c088 100644 --- a/Rayson.CV/docker-compose.prod.full.yml +++ b/Rayson.CV/docker-compose.prod.full.yml @@ -2,32 +2,10 @@ # Docker Compose - Production: Full Stack # =========================================== # Usage: docker compose -f docker-compose.prod.full.yml --env-file .env.production up -d -# Purpose: Run all services (PostgreSQL, API, UI) in Docker for production +# Purpose: Run all services (API, UI, Ollama) in Docker for production # =========================================== services: - # =========================================== - # PostgreSQL Database - # =========================================== - postgres: - image: pgvector/pgvector:pg16 - container_name: raysoncv-postgres - restart: unless-stopped - environment: - POSTGRES_DB: raysoncv - POSTGRES_USER: postgres - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - volumes: - - postgres-data:/var/lib/postgresql/data - networks: - - internal - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres -d raysoncv"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - # =========================================== # API (.NET 8.0) # =========================================== @@ -38,11 +16,6 @@ services: container_name: raysoncv-api restart: unless-stopped environment: - POSTGRES_HOST: postgres - POSTGRES_PORT: 5432 - POSTGRES_DATABASE: raysoncv - POSTGRES_USERNAME: postgres - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} ASPNETCORE_ENVIRONMENT: Production Cors__AllowedOrigins: https://www.rayson.dev,https://rayson.dev Ollama__Url: http://ollama:11434 @@ -50,8 +23,6 @@ services: ports: - "13245:8080" depends_on: - postgres: - condition: service_healthy ollama: condition: service_healthy networks: @@ -89,7 +60,7 @@ services: interval: 30s timeout: 10s retries: 3 - start_period: 30s + start_period: 10s # =========================================== # Ollama (AI Chatbot) @@ -131,5 +102,4 @@ networks: # Volumes # =========================================== volumes: - postgres-data: ollama_data: From 2938ac8d21b5d3dbe300a26e3a8555d22249f2d4 Mon Sep 17 00:00:00 2001 From: Daniel William Frederick Rayson Date: Wed, 18 Mar 2026 15:50:05 +0000 Subject: [PATCH 5/8] remove nomic for emebddings from ollama requirements --- .../Infrastructure/Ollama/ollama.README.md | 13 +-- .../Ollama/ollama.docker-compose.yml | 2 +- Rayson.CV/Api/Infrastructure/ollama.README.md | 13 +-- .../Infrastructure/ollama.docker-compose.yml | 2 +- Rayson.CV/docker-compose.dev.db-ui.yml | 89 ------------------- Rayson.CV/docker-compose.dev.full.yml | 2 +- Rayson.CV/docker-compose.prod.full.yml | 2 +- 7 files changed, 12 insertions(+), 111 deletions(-) delete mode 100644 Rayson.CV/docker-compose.dev.db-ui.yml diff --git a/Rayson.CV/Api/Infrastructure/Ollama/ollama.README.md b/Rayson.CV/Api/Infrastructure/Ollama/ollama.README.md index 52d6ca1..9398e83 100755 --- a/Rayson.CV/Api/Infrastructure/Ollama/ollama.README.md +++ b/Rayson.CV/Api/Infrastructure/Ollama/ollama.README.md @@ -57,12 +57,7 @@ await foreach (var chunk in ollama.ChatStreamAsync( Console.Write(chunk.Message?.Content); } -// Generate embeddings -var embedding = await ollama.EmbedAsync("Hello", "nomic-embed-text"); -Console.WriteLine(embedding.Embedding.Count); -``` - -## Prerequisites +// ## Prerequisites ### NVIDIA GPU Support @@ -107,11 +102,11 @@ docker compose ps ### Using Multiple Models -By default, the container starts with `smollm:135m` and `nomic-embed-text`. To configure models, edit the `CUSTOM_MODELS` environment variable in `ollama.docker-compose.yml`: +By default, the container starts with `smollm:135m`. To configure models, edit the `CUSTOM_MODELS` environment variable in `ollama.docker-compose.yml`: ```yaml environment: - - CUSTOM_MODELS=["smollm:135m","nomic-embed-text"] + - CUSTOM_MODELS=["smollm:135m"] ``` The `CUSTOM_MODELS` environment variable accepts a JSON array of model names. The entrypoint script will check if each model exists and pull it only if missing. @@ -124,7 +119,7 @@ The `CUSTOM_MODELS` environment variable accepts a JSON array of model names. Th | Environment Variable | Default | Description | |---------------------|---------|-------------| -| `CUSTOM_MODELS` | `["smollm:135m","nomic-embed-text"]` | Models to check/pull on startup | +| `CUSTOM_MODELS` | `["smollm:135m"]` | Models to check/pull on startup | > **Note:** The `OLLAMA_HOST` in the Ollama container is hardcoded to `0.0.0.0` so connections from other containers are accepted. This could raise security issues for your environment. diff --git a/Rayson.CV/Api/Infrastructure/Ollama/ollama.docker-compose.yml b/Rayson.CV/Api/Infrastructure/Ollama/ollama.docker-compose.yml index cb0690c..10dbcce 100755 --- a/Rayson.CV/Api/Infrastructure/Ollama/ollama.docker-compose.yml +++ b/Rayson.CV/Api/Infrastructure/Ollama/ollama.docker-compose.yml @@ -4,7 +4,7 @@ services: context: . dockerfile: ollama.Dockerfile environment: - - CUSTOM_MODELS=["llama3.2:latest","nomic-embed-text"] + - CUSTOM_MODELS=["llama3.2:latest"] volumes: - ollama_data:/root/.ollama container_name: ollama diff --git a/Rayson.CV/Api/Infrastructure/ollama.README.md b/Rayson.CV/Api/Infrastructure/ollama.README.md index 52d6ca1..9398e83 100755 --- a/Rayson.CV/Api/Infrastructure/ollama.README.md +++ b/Rayson.CV/Api/Infrastructure/ollama.README.md @@ -57,12 +57,7 @@ await foreach (var chunk in ollama.ChatStreamAsync( Console.Write(chunk.Message?.Content); } -// Generate embeddings -var embedding = await ollama.EmbedAsync("Hello", "nomic-embed-text"); -Console.WriteLine(embedding.Embedding.Count); -``` - -## Prerequisites +// ## Prerequisites ### NVIDIA GPU Support @@ -107,11 +102,11 @@ docker compose ps ### Using Multiple Models -By default, the container starts with `smollm:135m` and `nomic-embed-text`. To configure models, edit the `CUSTOM_MODELS` environment variable in `ollama.docker-compose.yml`: +By default, the container starts with `smollm:135m`. To configure models, edit the `CUSTOM_MODELS` environment variable in `ollama.docker-compose.yml`: ```yaml environment: - - CUSTOM_MODELS=["smollm:135m","nomic-embed-text"] + - CUSTOM_MODELS=["smollm:135m"] ``` The `CUSTOM_MODELS` environment variable accepts a JSON array of model names. The entrypoint script will check if each model exists and pull it only if missing. @@ -124,7 +119,7 @@ The `CUSTOM_MODELS` environment variable accepts a JSON array of model names. Th | Environment Variable | Default | Description | |---------------------|---------|-------------| -| `CUSTOM_MODELS` | `["smollm:135m","nomic-embed-text"]` | Models to check/pull on startup | +| `CUSTOM_MODELS` | `["smollm:135m"]` | Models to check/pull on startup | > **Note:** The `OLLAMA_HOST` in the Ollama container is hardcoded to `0.0.0.0` so connections from other containers are accepted. This could raise security issues for your environment. diff --git a/Rayson.CV/Api/Infrastructure/ollama.docker-compose.yml b/Rayson.CV/Api/Infrastructure/ollama.docker-compose.yml index d5ae730..a243ba2 100755 --- a/Rayson.CV/Api/Infrastructure/ollama.docker-compose.yml +++ b/Rayson.CV/Api/Infrastructure/ollama.docker-compose.yml @@ -4,7 +4,7 @@ services: context: . dockerfile: ollama.Dockerfile environment: - - CUSTOM_MODELS=["smollm2:135m","nomic-embed-text"] + - CUSTOM_MODELS=["smollm2:135m"] volumes: - ollama_data:/root/.ollama container_name: ollama diff --git a/Rayson.CV/docker-compose.dev.db-ui.yml b/Rayson.CV/docker-compose.dev.db-ui.yml deleted file mode 100644 index 34725aa..0000000 --- a/Rayson.CV/docker-compose.dev.db-ui.yml +++ /dev/null @@ -1,89 +0,0 @@ -# =========================================== -# Docker Compose - Development: Database + UI -# =========================================== -# Usage: docker compose -f docker-compose.dev.db-ui.yml up -d -# Purpose: Run PostgreSQL and UI in Docker, debug API locally -# API runs on host at http://localhost:13245 -# =========================================== - -services: - postgres: - image: pgvector/pgvector:pg16 - container_name: raysoncv-postgres - restart: unless-stopped - environment: - POSTGRES_DB: raysoncv - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - volumes: - - postgres-data:/var/lib/postgresql/data - ports: - - "5433:5432" - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres -d raysoncv"] - interval: 10s - timeout: 5s - retries: 5 - start_period: 10s - - ui: - build: - context: ./UI - dockerfile: Dockerfile - args: - VITE_API_BASE_URL: http://localhost:13245/ - container_name: raysoncv-ui - restart: unless-stopped - environment: - API_HEALTH_URL: http://host.docker.internal:13245/health - LOG_LEVEL: info - ports: - - "3000:3000" - extra_hosts: - - "host.docker.internal:host-gateway" - depends_on: - postgres: - condition: service_healthy - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:3000/health/live"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 10s - - # =========================================== - # Ollama (AI Chatbot) - # =========================================== - ollama: - build: - context: ./Api/Infrastructure/Ollama - dockerfile: ollama.Dockerfile - environment: - - CUSTOM_MODELS=["llama3.2:latest","nomic-embed-text"] - volumes: - - ollama_data:/root/.ollama - container_name: ollama - healthcheck: - test: ["CMD", "/ollama.healthcheck.sh"] - interval: 10s - timeout: 10s - retries: 30 - start_period: 10s - networks: - - internal - # NVIDIA GPU Support: Uncomment the following to enable NVIDIA GPU - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: all - capabilities: [gpu] - -volumes: - postgres-data: - ollama_data: - -networks: - internal: - driver: bridge diff --git a/Rayson.CV/docker-compose.dev.full.yml b/Rayson.CV/docker-compose.dev.full.yml index 0f4afdf..2d393c6 100644 --- a/Rayson.CV/docker-compose.dev.full.yml +++ b/Rayson.CV/docker-compose.dev.full.yml @@ -71,7 +71,7 @@ services: context: ./Api/Infrastructure/Ollama dockerfile: ollama.Dockerfile environment: - - CUSTOM_MODELS=["llama3.2:latest","nomic-embed-text"] + - CUSTOM_MODELS=["llama3.2:latest"] volumes: - ollama_data:/root/.ollama container_name: ollama diff --git a/Rayson.CV/docker-compose.prod.full.yml b/Rayson.CV/docker-compose.prod.full.yml index 259c088..a45b9c7 100644 --- a/Rayson.CV/docker-compose.prod.full.yml +++ b/Rayson.CV/docker-compose.prod.full.yml @@ -70,7 +70,7 @@ services: context: ./Api/Infrastructure/Ollama dockerfile: ollama.Dockerfile environment: - - CUSTOM_MODELS=["llama3.2:latest","nomic-embed-text"] + - CUSTOM_MODELS=["llama3.2:latest"] volumes: - ollama_data:/root/.ollama container_name: ollama From 62318dadf669ab9531f37c922f8a4d8ae200b401 Mon Sep 17 00:00:00 2001 From: Daniel William Frederick Rayson Date: Wed, 18 Mar 2026 16:54:44 +0000 Subject: [PATCH 6/8] cv updated --- Rayson.CV/Api/Domain/Resources/cv.md | 15 +++++++----- .../Endpoints/Files/StaticFilesEndpoints.cs | 2 +- Rayson.CV/Api/wwwroot/CV 2026.pdf | Bin 0 -> 129064 bytes .../Api/wwwroot/Daniel-Rayson-DotNet-CV.pdf | Bin 134241 -> 0 bytes Rayson.CV/UI/src/data/cv.ts | 23 ++++++++++-------- 5 files changed, 23 insertions(+), 17 deletions(-) create mode 100644 Rayson.CV/Api/wwwroot/CV 2026.pdf delete mode 100644 Rayson.CV/Api/wwwroot/Daniel-Rayson-DotNet-CV.pdf diff --git a/Rayson.CV/Api/Domain/Resources/cv.md b/Rayson.CV/Api/Domain/Resources/cv.md index 9fce328..0f8581d 100644 --- a/Rayson.CV/Api/Domain/Resources/cv.md +++ b/Rayson.CV/Api/Domain/Resources/cv.md @@ -4,7 +4,7 @@ Senior DotNet Developer PERSONAL DETAILS -ADDRESS | 85 Bagots Oak, ST17 9SB +ADDRESS | Stafford, ST17 9SB TELEPHONE | 07703 574867 EMAIL | daniel@rayson.dev WEBSITE | www.rayson.dev @@ -70,11 +70,14 @@ Title: Software Developer • Developed B2B APIs deployed to the Cloud using CI • Responsible for communicating with clients and work team -Self Employed (Material Product Arbitrage) 2023 - Onwards -Title: N/A - • Self-directed research and systems design - • Responsibility for following regulatory requirements - • Automation, data validation, service interfacing +Freelance 2024 - Current +Title: Senior Full-Stack Developer + • Followed ISO 29110 processes to ensure quality delivery + • Cloud and On-site hosting solutions with full CI/CD pipelines + • Full project lifecycle ownership from requirements through deployment + • Completed full-stack applications: arbitrage platform and custom e-commerce shop + • Contract and legal compliance considerations in project delivery + • Self-hosted portfolio website (www.rayson.dev) and additional personal projects HOME EXPERIENCE diff --git a/Rayson.CV/Api/Presentation/Endpoints/Files/StaticFilesEndpoints.cs b/Rayson.CV/Api/Presentation/Endpoints/Files/StaticFilesEndpoints.cs index a4efb83..cb37612 100644 --- a/Rayson.CV/Api/Presentation/Endpoints/Files/StaticFilesEndpoints.cs +++ b/Rayson.CV/Api/Presentation/Endpoints/Files/StaticFilesEndpoints.cs @@ -20,7 +20,7 @@ public static void MapStaticFilesEndpoints(this WebApplication webApplication) private static IResult GetCv() { - const string filename = "Daniel-Rayson-DotNet-CV.pdf"; + const string filename = "CV 2026.pdf"; var webRoot = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot"); var filePath = Path.Combine(webRoot, filename); diff --git a/Rayson.CV/Api/wwwroot/CV 2026.pdf b/Rayson.CV/Api/wwwroot/CV 2026.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5f37ffe21951aa49e2342820cec1a29fc1fdf672 GIT binary patch literal 129064 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਋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 O{GzO*Gs)UQ`M&^t{oW@4 literal 0 HcmV?d00001 diff --git a/Rayson.CV/Api/wwwroot/Daniel-Rayson-DotNet-CV.pdf b/Rayson.CV/Api/wwwroot/Daniel-Rayson-DotNet-CV.pdf deleted file mode 100644 index c44f9e3de0484fa9ea20a659c51138a8bf5b5e70..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 134241 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 z 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ΐ>ayW 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 diff --git a/Rayson.CV/UI/src/data/cv.ts b/Rayson.CV/UI/src/data/cv.ts index 6429423..94a0f68 100644 --- a/Rayson.CV/UI/src/data/cv.ts +++ b/Rayson.CV/UI/src/data/cv.ts @@ -38,7 +38,7 @@ export interface PersonalDetails { } export const personalDetails: PersonalDetails = { - address: '85 Bagots Oak, ST17 9SB', + address: 'Stafford, ST17 9SB', phone: '07703 574867', email: 'daniel@rayson.dev', website: 'www.rayson.dev', @@ -46,16 +46,19 @@ export const personalDetails: PersonalDetails = { export const workExperience: WorkExperience[] = [ { - id: 'self-employed', - company: 'Self Employed (Material Product Arbitrage)', - period: '2023 - Onwards', - yearStart: 2023, - title: 'N/A', - description: 'Self-directed research and systems design in material product arbitrage', + id: 'freelance', + company: 'Freelance', + period: '2024 - Current', + yearStart: 2024, + title: 'Senior Full-Stack Developer', + description: 'Full-stack development with focus on quality delivery and project ownership', bulletPoints: [ - 'Self-directed research and systems design', - 'Responsibility for following regulatory requirements', - 'Automation, data validation, service interfacing', + 'Followed ISO 29110 processes to ensure quality delivery', + 'Cloud and On-site hosting solutions with full CI/CD pipelines', + 'Full project lifecycle ownership from requirements through deployment', + 'Completed full-stack applications: arbitrage platform and custom e-commerce shop', + 'Contract and legal compliance considerations in project delivery', + 'Self-hosted portfolio website (www.rayson.dev) and additional personal projects', ], }, { From abde25565c03cd2c159555b66468fd3953992fa0 Mon Sep 17 00:00:00 2001 From: Daniel William Frederick Rayson Date: Wed, 18 Mar 2026 16:57:25 +0000 Subject: [PATCH 7/8] better alignment on pdf --- Rayson.CV/Api/wwwroot/CV 2026.pdf | Bin 129064 -> 129059 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Rayson.CV/Api/wwwroot/CV 2026.pdf b/Rayson.CV/Api/wwwroot/CV 2026.pdf index 5f37ffe21951aa49e2342820cec1a29fc1fdf672..69cada3a8114224d8cfe265a31f95150d53525f2 100644 GIT binary patch delta 24996 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 Date: Wed, 18 Mar 2026 17:01:58 +0000 Subject: [PATCH 8/8] corrected timieline data --- Rayson.CV/UI/src/data/cv.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Rayson.CV/UI/src/data/cv.ts b/Rayson.CV/UI/src/data/cv.ts index 94a0f68..2d3fd21 100644 --- a/Rayson.CV/UI/src/data/cv.ts +++ b/Rayson.CV/UI/src/data/cv.ts @@ -191,8 +191,8 @@ export const projects: Project[] = [ id: 'tradepulse', name: 'TradePulse', description: 'Trading API project', - period: '2024', - year: 2024, + period: '2023', + year: 2023, technologies: ['.NET', 'API'], type: 'application', }, @@ -200,8 +200,8 @@ export const projects: Project[] = [ id: 'tradepulse3', name: 'TradePulse3', description: 'Trading system v3', - period: '2023', - year: 2023, + period: '2024', + year: 2024, technologies: ['.NET', 'React'], type: 'application', }, @@ -209,8 +209,8 @@ export const projects: Project[] = [ id: 'rayson-cv', name: 'Rayson.CV', description: 'Personal CV website', - period: '2023', - year: 2023, + period: '2025', + year: 2025, technologies: ['React', 'TypeScript', 'Vite'], type: 'application', },