From 6a1bf8e61dbae10bdd93aaff5d46181bf67e2e0d Mon Sep 17 00:00:00 2001 From: phact Date: Tue, 7 Oct 2025 11:32:04 -0400 Subject: [PATCH 01/21] tui ip bind check --- src/utils/container_utils.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/utils/container_utils.py b/src/utils/container_utils.py index 14222c84..746379e8 100644 --- a/src/utils/container_utils.py +++ b/src/utils/container_utils.py @@ -157,10 +157,22 @@ def guess_host_ip_for_containers(logger=None) -> str: import logging import re import shutil + import socket import subprocess log = logger or logging.getLogger(__name__) + def can_bind_to_address(ip_addr: str) -> bool: + """Test if we can bind to the given IP address.""" + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind((ip_addr, 0)) # Port 0 = let OS choose a free port + return True + except (OSError, socket.error) as e: + log.debug("Cannot bind to %s: %s", ip_addr, e) + return False + def run(cmd, timeout=2, text=True): return subprocess.run(cmd, capture_output=True, text=text, timeout=timeout) @@ -261,10 +273,23 @@ def guess_host_ip_for_containers(logger=None) -> str: "Container-reachable host IP candidates: %s", ", ".join(ordered_candidates), ) - else: - log.info("Container-reachable host IP: %s", ordered_candidates[0]) - return ordered_candidates[0] + # Try each candidate and return the first one we can bind to + for ip_addr in ordered_candidates: + if can_bind_to_address(ip_addr): + if len(ordered_candidates) > 1: + log.info("Selected bindable host IP: %s", ip_addr) + else: + log.info("Container-reachable host IP: %s", ip_addr) + return ip_addr + log.debug("Skipping %s (cannot bind)", ip_addr) + + # None of the candidates were bindable, fall back to 127.0.0.1 + log.warning( + "None of the discovered IPs (%s) can be bound; falling back to 127.0.0.1", + ", ".join(ordered_candidates), + ) + return "127.0.0.1" log.warning( "No container bridge IP found. For rootless Podman (slirp4netns) there may be no host bridge; publish ports or use 10.0.2.2 from the container." From c5894b719e26a33ddf8fded58d230b25cdf5dff5 Mon Sep 17 00:00:00 2001 From: Cole Goldsmith Date: Tue, 7 Oct 2025 10:41:05 -0500 Subject: [PATCH 02/21] chunks page issues --- frontend/src/app/knowledge/chunks/page.tsx | 169 ++++++++++----------- 1 file changed, 83 insertions(+), 86 deletions(-) diff --git a/frontend/src/app/knowledge/chunks/page.tsx b/frontend/src/app/knowledge/chunks/page.tsx index 6da3dc5e..ea00d3ad 100644 --- a/frontend/src/app/knowledge/chunks/page.tsx +++ b/frontend/src/app/knowledge/chunks/page.tsx @@ -5,14 +5,9 @@ import { useRouter, useSearchParams } from "next/navigation"; import { Suspense, useCallback, useEffect, useMemo, useState } from "react"; // import { Label } from "@/components/ui/label"; // import { Checkbox } from "@/components/ui/checkbox"; -import { filterAccentClasses } from "@/components/knowledge-filter-panel"; import { ProtectedRoute } from "@/components/protected-route"; import { Button } from "@/components/ui/button"; -import { Checkbox } from "@/components/ui/checkbox"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; import { useKnowledgeFilter } from "@/contexts/knowledge-filter-context"; -import { useTask } from "@/contexts/task-context"; import { type ChunkResult, type File, @@ -35,9 +30,9 @@ function ChunksPageContent() { const { parsedFilterData, queryOverride } = useKnowledgeFilter(); const filename = searchParams.get("filename"); const [chunks, setChunks] = useState([]); - const [chunksFilteredByQuery, setChunksFilteredByQuery] = useState< - ChunkResult[] - >([]); + // const [chunksFilteredByQuery, setChunksFilteredByQuery] = useState< + // ChunkResult[] + // >([]); // const [selectedChunks, setSelectedChunks] = useState>(new Set()); const [activeCopiedChunkIndex, setActiveCopiedChunkIndex] = useState< number | null @@ -126,26 +121,25 @@ function ChunksPageContent() { return (
-
- {/* Header */} -
-
- -

- {/* Removes file extension from filename */} - {filename.replace(/\.[^/.]+$/, "")} -

-
-
- - {/*
+ {/* Header */} +
+
+ +

+ {/* Removes file extension from filename */} + {filename.replace(/\.[^/.]+$/, "")} +

+
+
+ + {/*
*/} -
+
- {/* Content Area - matches knowledge page structure */} -
+
+ {/* Content Area */} +
{isFetching ? (
@@ -185,7 +180,7 @@ function ChunksPageContent() {
) : (
- {chunksFilteredByQuery.map((chunk, index) => ( + {chunks.map((chunk, index) => (
)}
-
- {/* Right panel - Summary (TODO), Technical details, */} - {chunks.length > 0 && ( -
-
-

- Technical details -

-
-
-
- Total chunks -
-
- {chunks.length} -
-
-
-
Avg length
-
- {averageChunkLength.toFixed(0)} chars -
-
- {/* TODO: Uncomment after data is available */} - {/*
+ {/* Right panel - Summary (TODO), Technical details, */} + {chunks.length > 0 && ( +
+
+

+ Technical details +

+
+
+
+ Total chunks +
+
+ {chunks.length} +
+
+
+
+ Avg length +
+
+ {averageChunkLength.toFixed(0)} chars +
+
+ {/* TODO: Uncomment after data is available */} + {/*
Process time
@@ -276,54 +272,55 @@ function ChunksPageContent() {
*/} -
-
-
-

- Original document -

-
- {/*
+
+
+
+

+ Original document +

+
+ {/*
Name
{fileData?.filename}
*/} -
-
Type
-
- {fileData ? getFileTypeLabel(fileData.mimetype) : "Unknown"} -
-
-
-
Size
-
- {fileData?.size - ? `${Math.round(fileData.size / 1024)} KB` - : "Unknown"} -
-
- {/*
+
+
Type
+
+ {fileData ? getFileTypeLabel(fileData.mimetype) : "Unknown"} +
+
+
+
Size
+
+ {fileData?.size + ? `${Math.round(fileData.size / 1024)} KB` + : "Unknown"} +
+
+ {/*
Uploaded
N/A
*/} - {/* TODO: Uncomment after data is available */} - {/*
+ {/* TODO: Uncomment after data is available */} + {/*
Source
*/} - {/*
+ {/*
Updated
N/A
*/} -
+
+
-
- )} + )} +
); } From 84a4236466b6e07a45d414f4c9ff93ba4795377d Mon Sep 17 00:00:00 2001 From: Cole Goldsmith Date: Tue, 7 Oct 2025 10:47:56 -0500 Subject: [PATCH 03/21] search styles, chunk padding --- frontend/components/knowledge-search-input.tsx | 4 ++-- frontend/src/app/knowledge/chunks/page.tsx | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/frontend/components/knowledge-search-input.tsx b/frontend/components/knowledge-search-input.tsx index fd840628..5568964c 100644 --- a/frontend/components/knowledge-search-input.tsx +++ b/frontend/components/knowledge-search-input.tsx @@ -74,7 +74,7 @@ export const KnowledgeSearchInput = () => { {queryOverride && (