fraq 0.2.2__tar.gz → 0.2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. {fraq-0.2.2 → fraq-0.2.4}/CHANGELOG.md +51 -0
  2. {fraq-0.2.2 → fraq-0.2.4}/PKG-INFO +6 -2
  3. {fraq-0.2.2 → fraq-0.2.4}/README.md +5 -1
  4. fraq-0.2.4/examples/cli-docker/run.py +63 -0
  5. fraq-0.2.4/examples/fastapi-docker/main.py +86 -0
  6. fraq-0.2.4/examples/fastapi-docker/run.py +98 -0
  7. fraq-0.2.4/examples/fullstack-docker/api/main.py +38 -0
  8. fraq-0.2.4/examples/fullstack-docker/frontend/app.py +60 -0
  9. fraq-0.2.4/examples/fullstack-docker/run.py +76 -0
  10. fraq-0.2.4/examples/fullstack-docker/websocket/main.py +52 -0
  11. fraq-0.2.4/examples/websocket-docker/main.py +104 -0
  12. fraq-0.2.4/examples/websocket-docker/run.py +88 -0
  13. {fraq-0.2.2 → fraq-0.2.4}/fraq/__init__.py +1 -1
  14. {fraq-0.2.2 → fraq-0.2.4}/fraq/cli.py +27 -21
  15. {fraq-0.2.2 → fraq-0.2.4}/fraq/text2fraq.py +193 -277
  16. {fraq-0.2.2 → fraq-0.2.4}/fraq.egg-info/SOURCES.txt +9 -0
  17. {fraq-0.2.2 → fraq-0.2.4}/pyproject.toml +1 -1
  18. {fraq-0.2.2 → fraq-0.2.4}/tests/test_text2fraq.py +78 -1
  19. {fraq-0.2.2 → fraq-0.2.4}/LICENSE +0 -0
  20. {fraq-0.2.2 → fraq-0.2.4}/MANIFEST.in +0 -0
  21. {fraq-0.2.2 → fraq-0.2.4}/examples/api_server.py +0 -0
  22. {fraq-0.2.2 → fraq-0.2.4}/examples/app_integrations.py +0 -0
  23. {fraq-0.2.2 → fraq-0.2.4}/examples/applications.py +0 -0
  24. {fraq-0.2.2 → fraq-0.2.4}/examples/async_streaming.py +0 -0
  25. {fraq-0.2.2 → fraq-0.2.4}/examples/nlp2cmd_integration.py +0 -0
  26. {fraq-0.2.2 → fraq-0.2.4}/examples/query_examples.py +0 -0
  27. {fraq-0.2.2 → fraq-0.2.4}/examples/text2fraq_examples.py +0 -0
  28. {fraq-0.2.2 → fraq-0.2.4}/examples/text2fraq_files.py +0 -0
  29. {fraq-0.2.2 → fraq-0.2.4}/fraq/adapters.py +0 -0
  30. {fraq-0.2.2 → fraq-0.2.4}/fraq/core.py +0 -0
  31. {fraq-0.2.2 → fraq-0.2.4}/fraq/formats.py +0 -0
  32. {fraq-0.2.2 → fraq-0.2.4}/fraq/generators.py +0 -0
  33. {fraq-0.2.2 → fraq-0.2.4}/fraq/py.typed +0 -0
  34. {fraq-0.2.2 → fraq-0.2.4}/fraq/query.py +0 -0
  35. {fraq-0.2.2 → fraq-0.2.4}/fraq/schema_export.py +0 -0
  36. {fraq-0.2.2 → fraq-0.2.4}/fraq/streaming.py +0 -0
  37. {fraq-0.2.2 → fraq-0.2.4}/setup.cfg +0 -0
  38. {fraq-0.2.2 → fraq-0.2.4}/tests/__init__.py +0 -0
  39. {fraq-0.2.2 → fraq-0.2.4}/tests/test_adapters.py +0 -0
  40. {fraq-0.2.2 → fraq-0.2.4}/tests/test_cli.py +0 -0
  41. {fraq-0.2.2 → fraq-0.2.4}/tests/test_core.py +0 -0
  42. {fraq-0.2.2 → fraq-0.2.4}/tests/test_formats.py +0 -0
  43. {fraq-0.2.2 → fraq-0.2.4}/tests/test_generators.py +0 -0
  44. {fraq-0.2.2 → fraq-0.2.4}/tests/test_query.py +0 -0
  45. {fraq-0.2.2 → fraq-0.2.4}/tests/test_schema_export.py +0 -0
  46. {fraq-0.2.2 → fraq-0.2.4}/tests/test_streaming.py +0 -0
@@ -2,6 +2,57 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.2.4] - 2026-03-17
6
+
7
+ ### Docs
8
+ - Update README.md
9
+ - Update docs/README.md
10
+ - Update examples/README.md
11
+ - Update examples/cli-docker/README.md
12
+ - Update examples/fastapi-docker/README.md
13
+ - Update examples/websocket-docker/README.md
14
+ - Update project/README.md
15
+ - Update project/context.md
16
+
17
+ ### Test
18
+ - Update tests/test_text2fraq.py
19
+
20
+ ### Other
21
+ - Update examples/cli-docker/run.py
22
+ - Update examples/cli-docker/run.sh
23
+ - Update examples/fastapi-docker/run.sh
24
+ - Update examples/fullstack-docker/run.py
25
+ - Update examples/fullstack-docker/run.sh
26
+ - Update examples/websocket-docker/run.py
27
+ - Update examples/websocket-docker/run.sh
28
+ - Update fraq/cli.py
29
+ - Update fraq/text2fraq.py
30
+ - Update project/analysis.toon
31
+ - ... and 11 more files
32
+
33
+ ## [0.2.3] - 2026-03-17
34
+
35
+ ### Docs
36
+ - Update docs/README.md
37
+ - Update examples/cli-docker/README.md
38
+ - Update examples/fastapi-docker/README.md
39
+ - Update examples/fullstack-docker/README.md
40
+ - Update examples/websocket-docker/README.md
41
+ - Update project/context.md
42
+
43
+ ### Other
44
+ - Update Dockerfile.cli
45
+ - Update Dockerfile.websocket
46
+ - Update examples/cli-docker/Dockerfile
47
+ - Update examples/cli-docker/docker-compose.yml
48
+ - Update examples/cli-docker/run.sh
49
+ - Update examples/fastapi-docker/Dockerfile
50
+ - Update examples/fastapi-docker/docker-compose.yml
51
+ - Update examples/fastapi-docker/main.py
52
+ - Update examples/fastapi-docker/run.py
53
+ - Update examples/fastapi-docker/run.sh
54
+ - ... and 21 more files
55
+
5
56
  ## [0.2.2] - 2026-03-17
6
57
 
7
58
  ### Docs
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fraq
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Fractal Query Data Library — model data as infinite, self-similar fractal structures
5
5
  Author: Softreck / Prototypowanie.pl
6
6
  Author-email: Tom Sapletta <tom@sapletta.com>
@@ -95,6 +95,7 @@ pip install -e ".[dev]"
95
95
  fraq explore --dims 3 --depth 5 --format json
96
96
  fraq stream --dims 2 --count 20 --format csv
97
97
  fraq schema --fields "name:str,value:float,flag:bool" --depth 2
98
+ fraq nl "show 10 pdf files created recently" --path .
98
99
 
99
100
  # Docker
100
101
  docker compose run test # run full test suite
@@ -123,7 +124,7 @@ q = (
123
124
  result = FraqExecutor(dims=3).execute(q)
124
125
 
125
126
  # 3. Source adapters — same query, different sources
126
- from fraq import FileAdapter, SQLAdapter, SensorAdapter, HybridAdapter
127
+ from fraq import FileAdapter, SQLAdapter, SensorAdapter, HybridAdapter, FileSearchAdapter
127
128
 
128
129
  # Disk
129
130
  adapter = FileAdapter()
@@ -144,6 +145,9 @@ hybrid.add(FileAdapter(), "local_backup.json")
144
145
  hybrid.add(SensorAdapter(), "")
145
146
  merged = hybrid.load_root()
146
147
 
148
+ # Files
149
+ files = FileSearchAdapter(base_path=".").search(extension="py", limit=5, sort_by="mtime")
150
+
147
151
  # 4. Async streaming (FastAPI SSE, Kafka, NATS)
148
152
  from fraq.streaming import async_stream
149
153
  async for record in async_stream(count=1000, interval=0.1):
@@ -42,6 +42,7 @@ pip install -e ".[dev]"
42
42
  fraq explore --dims 3 --depth 5 --format json
43
43
  fraq stream --dims 2 --count 20 --format csv
44
44
  fraq schema --fields "name:str,value:float,flag:bool" --depth 2
45
+ fraq nl "show 10 pdf files created recently" --path .
45
46
 
46
47
  # Docker
47
48
  docker compose run test # run full test suite
@@ -70,7 +71,7 @@ q = (
70
71
  result = FraqExecutor(dims=3).execute(q)
71
72
 
72
73
  # 3. Source adapters — same query, different sources
73
- from fraq import FileAdapter, SQLAdapter, SensorAdapter, HybridAdapter
74
+ from fraq import FileAdapter, SQLAdapter, SensorAdapter, HybridAdapter, FileSearchAdapter
74
75
 
75
76
  # Disk
76
77
  adapter = FileAdapter()
@@ -91,6 +92,9 @@ hybrid.add(FileAdapter(), "local_backup.json")
91
92
  hybrid.add(SensorAdapter(), "")
92
93
  merged = hybrid.load_root()
93
94
 
95
+ # Files
96
+ files = FileSearchAdapter(base_path=".").search(extension="py", limit=5, sort_by="mtime")
97
+
94
98
  # 4. Async streaming (FastAPI SSE, Kafka, NATS)
95
99
  from fraq.streaming import async_stream
96
100
  async for record in async_stream(count=1000, interval=0.1):
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ CLI Docker — Wrapper do uruchamiania fraq CLI w Dockerze
4
+
5
+ Użycie:
6
+ python run.py files search --ext pdf --limit 10 /data
7
+ python run.py nl "pokaż 10 plików"
8
+ python run.py --local explore --depth 5 # lokalnie, bez Docker
9
+ """
10
+
11
+ import argparse
12
+ import subprocess
13
+ import sys
14
+ import os
15
+
16
+
17
+ def run_in_docker(args_list):
18
+ """Uruchom fraq CLI w Docker"""
19
+ cmd = ["docker-compose", "run", "--rm", "fraq-cli"] + args_list
20
+
21
+ print(f"🐳 Docker: {' '.join(cmd)}")
22
+ print("")
23
+
24
+ result = subprocess.run(cmd)
25
+ return result.returncode
26
+
27
+
28
+ def run_local(args_list):
29
+ """Uruchom fraq CLI lokalnie"""
30
+ cmd = ["fraq"] + args_list
31
+
32
+ print(f"🌀 Local: {' '.join(cmd)}")
33
+ print("")
34
+
35
+ result = subprocess.run(cmd)
36
+ return result.returncode
37
+
38
+
39
+ def main():
40
+ # Parsuj argumenty - przekaż wszystko do fraq
41
+ parser = argparse.ArgumentParser(
42
+ description="CLI Docker wrapper",
43
+ add_help=False
44
+ )
45
+ parser.add_argument("--local", action="store_true", help="Uruchom lokalnie (bez Docker)")
46
+ parser.add_argument("--docker", action="store_true", help="Wymuś Docker")
47
+
48
+ # Znajdź --local/--docker przed przekazaniem reszty
49
+ args, remaining = parser.parse_known_args()
50
+
51
+ # Jeśli nie ma argumentów, pokaż help
52
+ if not remaining:
53
+ remaining = ["--help"]
54
+
55
+ # Uruchom
56
+ if args.local:
57
+ return run_local(remaining)
58
+ else:
59
+ return run_in_docker(remaining)
60
+
61
+
62
+ if __name__ == "__main__":
63
+ sys.exit(main())
@@ -0,0 +1,86 @@
1
+ """
2
+ FastAPI + fraq Docker example
3
+ Minimal REST API for fractal queries and file search
4
+ """
5
+
6
+ from fastapi import FastAPI, Query
7
+ from fastapi.responses import JSONResponse
8
+ import os
9
+
10
+ from fraq import FileSearchAdapter, FraqNode, FraqExecutor, FraqQuery
11
+ from fraq.generators import HashGenerator
12
+ from fraq.formats import FormatRegistry
13
+
14
+ app = FastAPI(title="fraq Docker API", version="0.2.2")
15
+
16
+ # Config from env
17
+ FRAQ_DIMS = int(os.getenv("FRAQ_DIMS", "3"))
18
+ FRAQ_SEED = int(os.getenv("FRAQ_SEED", "0"))
19
+
20
+
21
+ @app.get("/")
22
+ def root():
23
+ return {"service": "fraq-docker", "version": "0.2.2", "dims": FRAQ_DIMS}
24
+
25
+
26
+ @app.get("/health")
27
+ def health():
28
+ return {"status": "ok"}
29
+
30
+
31
+ @app.get("/explore")
32
+ def explore(
33
+ depth: int = Query(3, ge=1, le=20),
34
+ dims: int = Query(FRAQ_DIMS, ge=1, le=10),
35
+ format: str = Query("json"),
36
+ ):
37
+ """Explore fractal structure"""
38
+ pos = tuple(0.0 for _ in range(dims))
39
+ root = FraqNode(position=pos, seed=FRAQ_SEED, generator=HashGenerator())
40
+ node = root.zoom(steps=depth)
41
+ data = node.to_dict(max_depth=1)
42
+ return JSONResponse(content=data)
43
+
44
+
45
+ @app.get("/files/search")
46
+ def files_search(
47
+ path: str = Query("/host/home", description="Directory to search"),
48
+ ext: str | None = Query(None, description="File extension"),
49
+ limit: int = Query(10, ge=1, le=1000),
50
+ sort: str = Query("mtime", regex="^(name|mtime|size)$"),
51
+ ):
52
+ """Search files with fractal metadata"""
53
+ try:
54
+ adapter = FileSearchAdapter(base_path=path, recursive=True)
55
+ results = adapter.search(extension=ext, limit=limit, sort_by=sort)
56
+ return {"path": path, "count": len(results), "files": results}
57
+ except Exception as e:
58
+ return {"error": str(e)}
59
+
60
+
61
+ @app.get("/files/stat/{file_path:path}")
62
+ def files_stat(file_path: str):
63
+ """Get file statistics with fractal coordinates"""
64
+ from pathlib import Path
65
+ from datetime import datetime
66
+
67
+ path = Path(file_path)
68
+ if not path.exists():
69
+ return {"error": "File not found"}
70
+
71
+ stat = path.stat()
72
+ return {
73
+ "filename": path.name,
74
+ "path": str(path.absolute()),
75
+ "size": stat.st_size,
76
+ "mtime": datetime.fromtimestamp(stat.st_mtime).isoformat(),
77
+ "fraq": {
78
+ "seed": hash(str(path)) % (2**32),
79
+ "value": hash(str(path)) / (2**32),
80
+ }
81
+ }
82
+
83
+
84
+ if __name__ == "__main__":
85
+ import uvicorn
86
+ uvicorn.run(app, host="0.0.0.0", port=8000)
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ FastAPI Docker — Uruchomienie bez Docker (opcjonalnie)
4
+
5
+ Użycie:
6
+ python run.py # Uruchom serwer lokalnie (bez Docker)
7
+ python run.py --docker # Uruchom przez Docker
8
+ """
9
+
10
+ import argparse
11
+ import subprocess
12
+ import sys
13
+ import os
14
+
15
+
16
+ def run_local():
17
+ """Uruchom serwer lokalnie (bez Docker)"""
18
+ print("🌀 Uruchamianie FastAPI lokalnie...")
19
+ try:
20
+ import uvicorn
21
+ from main import app
22
+ uvicorn.run(app, host="0.0.0.0", port=8000)
23
+ except ImportError:
24
+ print("❌ Brakujące zależności. Zainstaluj:")
25
+ print(" pip install fraq fastapi uvicorn")
26
+ sys.exit(1)
27
+
28
+
29
+ def run_docker():
30
+ """Uruchom przez Docker"""
31
+ print("🐳 Uruchamianie przez Docker...")
32
+ subprocess.run(["docker-compose", "up", "--build", "-d"], check=True)
33
+ print("\n✅ Serwer działa na http://localhost:8000")
34
+ print("\nTestuj:")
35
+ print(" curl http://localhost:8000/health")
36
+
37
+
38
+ def stop_docker():
39
+ """Zatrzymaj Docker"""
40
+ print("🛑 Zatrzymywanie Dockera...")
41
+ subprocess.run(["docker-compose", "down"], check=True)
42
+ print("✅ Zatrzymano")
43
+
44
+
45
+ def test_api():
46
+ """Przetestuj API"""
47
+ import requests
48
+ import json
49
+
50
+ base = "http://localhost:8000"
51
+
52
+ print("🧪 Testowanie API...")
53
+
54
+ # Health
55
+ try:
56
+ r = requests.get(f"{base}/health", timeout=5)
57
+ print(f"✅ Health: {r.json()}")
58
+ except Exception as e:
59
+ print(f"❌ Health failed: {e}")
60
+ return
61
+
62
+ # Files search
63
+ try:
64
+ r = requests.get(f"{base}/files/search?ext=py&limit=3", timeout=10)
65
+ data = r.json()
66
+ print(f"✅ Files search: {data.get('count', 0)} files found")
67
+ except Exception as e:
68
+ print(f"❌ Files search failed: {e}")
69
+
70
+ # Explore
71
+ try:
72
+ r = requests.get(f"{base}/explore?depth=2", timeout=10)
73
+ data = r.json()
74
+ print(f"✅ Explore: fractal depth={data.get('depth', 'N/A')}")
75
+ except Exception as e:
76
+ print(f"❌ Explore failed: {e}")
77
+
78
+
79
+ def main():
80
+ parser = argparse.ArgumentParser(description="FastAPI + fraq runner")
81
+ parser.add_argument("--docker", action="store_true", help="Użyj Docker")
82
+ parser.add_argument("--stop", action="store_true", help="Zatrzymaj Docker")
83
+ parser.add_argument("--test", action="store_true", help="Przetestuj API")
84
+
85
+ args = parser.parse_args()
86
+
87
+ if args.stop:
88
+ stop_docker()
89
+ elif args.test:
90
+ test_api()
91
+ elif args.docker:
92
+ run_docker()
93
+ else:
94
+ run_local()
95
+
96
+
97
+ if __name__ == "__main__":
98
+ main()
@@ -0,0 +1,38 @@
1
+ """
2
+ Fullstack - API service
3
+ """
4
+ from fastapi import FastAPI, Query
5
+ from fastapi.responses import JSONResponse
6
+
7
+ from fraq import FileSearchAdapter, FraqNode
8
+ from fraq.generators import HashGenerator
9
+
10
+ app = FastAPI(title="fraq Fullstack API")
11
+
12
+
13
+ @app.get("/")
14
+ def root():
15
+ return {"service": "api", "version": "0.2.2"}
16
+
17
+
18
+ @app.get("/health")
19
+ def health():
20
+ return {"status": "ok"}
21
+
22
+
23
+ @app.get("/explore")
24
+ def explore(depth: int = Query(3)):
25
+ root = FraqNode(position=(0.0, 0.0, 0.0), generator=HashGenerator())
26
+ node = root.zoom(steps=depth)
27
+ return JSONResponse(content=node.to_dict(max_depth=1))
28
+
29
+
30
+ @app.get("/files/search")
31
+ def files_search(
32
+ path: str = Query("/data"),
33
+ ext: str | None = Query(None),
34
+ limit: int = Query(10),
35
+ ):
36
+ adapter = FileSearchAdapter(base_path=path, recursive=True)
37
+ results = adapter.search(extension=ext, limit=limit, sort_by="mtime")
38
+ return {"count": len(results), "files": results}
@@ -0,0 +1,60 @@
1
+ """
2
+ Fullstack - Streamlit Frontend
3
+ """
4
+ import streamlit as st
5
+ import requests
6
+ import json
7
+ import os
8
+
9
+ API_URL = os.getenv("API_URL", "http://localhost:8000")
10
+ WS_URL = os.getenv("WS_URL", "ws://localhost:8001")
11
+
12
+ st.set_page_config(page_title="fraq UI", layout="wide")
13
+
14
+ st.title("🌀 fraq — Fractal Query Data Library")
15
+
16
+ # Sidebar
17
+ st.sidebar.header("Konfiguracja")
18
+ path = st.sidebar.text_input("Ścieżka", "/data")
19
+ ext_filter = st.sidebar.text_input("Rozszerzenie", "py")
20
+ limit = st.sidebar.slider("Limit", 1, 100, 10)
21
+
22
+ # Tabs
23
+ tab1, tab2, tab3 = st.tabs(["🔍 Wyszukiwanie plików", "🌐 API REST", "ℹ️ Info"])
24
+
25
+ with tab1:
26
+ st.subheader("Wyszukiwanie plików")
27
+ if st.button("Szukaj"):
28
+ try:
29
+ resp = requests.get(f"{API_URL}/files/search", params={
30
+ "path": path, "ext": ext_filter or None, "limit": limit
31
+ })
32
+ data = resp.json()
33
+ if data.get("files"):
34
+ st.write(f"Znaleziono {data['count']} plików:")
35
+ for f in data["files"]:
36
+ st.code(f"{f['filename']} ({f['size']} bytes)")
37
+ else:
38
+ st.warning("Brak wyników")
39
+ except Exception as e:
40
+ st.error(f"Błąd: {e}")
41
+
42
+ with tab2:
43
+ st.subheader("Test API")
44
+ st.code(f"GET {API_URL}/health", language="bash")
45
+ try:
46
+ health = requests.get(f"{API_URL}/health").json()
47
+ st.json(health)
48
+ except Exception as e:
49
+ st.error(f"API niedostępne: {e}")
50
+
51
+ with tab3:
52
+ st.subheader("API Endpoints")
53
+ st.markdown("""
54
+ - `GET /health` — Health check
55
+ - `GET /explore?depth=3` — Eksploracja fraktala
56
+ - `GET /files/search?ext=pdf&limit=10` — Wyszukiwanie plików
57
+ - `WS /ws/stream` — WebSocket streaming
58
+ """)
59
+ st.info(f"API URL: {API_URL}")
60
+ st.info(f"WebSocket URL: {WS_URL}")
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Fullstack Docker — Uruchomienie i zarządzanie stackiem
4
+
5
+ Użycie:
6
+ python run.py # Uruchom stack
7
+ python run.py --test # Przetestuj
8
+ python run.py --stop # Zatrzymaj
9
+ python run.py --local # Uruchom lokalnie (bez Docker)
10
+ """
11
+
12
+ import argparse
13
+ import subprocess
14
+ import sys
15
+ import time
16
+
17
+
18
+ def run_docker():
19
+ """Uruchom przez Docker Compose"""
20
+ print("🐳 Uruchamianie stacku...")
21
+ subprocess.run(["docker-compose", "up", "--build", "-d"], check=True)
22
+ print("\n✅ Stack działa:")
23
+ print(" API: http://localhost:8000")
24
+ print(" WebSocket: ws://localhost:8001")
25
+ print(" Frontend: http://localhost:8501")
26
+
27
+
28
+ def stop_docker():
29
+ """Zatrzymaj stack"""
30
+ print("🛑 Zatrzymywanie...")
31
+ subprocess.run(["docker-compose", "down"], check=True)
32
+ print("✅ Zatrzymano")
33
+
34
+
35
+ def test_stack():
36
+ """Przetestuj stack"""
37
+ import requests
38
+
39
+ print("🧪 Testowanie...")
40
+ time.sleep(2)
41
+
42
+ # Test API
43
+ try:
44
+ r = requests.get("http://localhost:8000/health", timeout=5)
45
+ print(f"✅ API: {r.json()}")
46
+ except Exception as e:
47
+ print(f"❌ API: {e}")
48
+
49
+ # Test Frontend
50
+ try:
51
+ r = requests.get("http://localhost:8501", timeout=5)
52
+ print(f"✅ Frontend: HTTP {r.status_code}")
53
+ except Exception as e:
54
+ print(f"❌ Frontend: {e}")
55
+
56
+
57
+ def main():
58
+ parser = argparse.ArgumentParser(description="Fullstack Docker runner")
59
+ parser.add_argument("--stop", action="store_true", help="Zatrzymaj")
60
+ parser.add_argument("--test", action="store_true", help="Przetestuj")
61
+ parser.add_argument("--logs", action="store_true", help="Pokaż logi")
62
+
63
+ args = parser.parse_args()
64
+
65
+ if args.stop:
66
+ stop_docker()
67
+ elif args.test:
68
+ test_stack()
69
+ elif args.logs:
70
+ subprocess.run(["docker-compose", "logs", "-f"])
71
+ else:
72
+ run_docker()
73
+
74
+
75
+ if __name__ == "__main__":
76
+ main()
@@ -0,0 +1,52 @@
1
+ """
2
+ Fullstack - WebSocket service
3
+ """
4
+ import asyncio
5
+ from fastapi import FastAPI, WebSocket, WebSocketDisconnect
6
+
7
+ from fraq import FileSearchAdapter, FraqNode
8
+ from fraq.generators import HashGenerator
9
+
10
+ app = FastAPI(title="fraq Fullstack WebSocket")
11
+
12
+
13
+ @app.websocket("/ws/stream")
14
+ async def ws_stream(websocket: WebSocket):
15
+ await websocket.accept()
16
+ try:
17
+ while True:
18
+ msg = await websocket.receive_json()
19
+ if msg.get("action") == "stream":
20
+ count = msg.get("count", 10)
21
+ root = FraqNode(position=(0.0, 0.0, 0.0), generator=HashGenerator())
22
+ cursor = root.cursor()
23
+ for i in range(count):
24
+ cursor.advance()
25
+ await websocket.send_json({"index": i, "value": cursor.current.value})
26
+ await asyncio.sleep(0.1)
27
+ except WebSocketDisconnect:
28
+ pass
29
+
30
+
31
+ @app.websocket("/ws/files")
32
+ async def ws_files(websocket: WebSocket):
33
+ await websocket.accept()
34
+ try:
35
+ while True:
36
+ msg = await websocket.receive_json()
37
+ if msg.get("action") == "search":
38
+ path = msg.get("path", "/data")
39
+ ext = msg.get("ext")
40
+ limit = msg.get("limit", 10)
41
+ adapter = FileSearchAdapter(base_path=path, recursive=True)
42
+ for record in adapter.stream(extension=ext, count=limit):
43
+ await websocket.send_json(record)
44
+ await asyncio.sleep(0.01)
45
+ await websocket.send_json({"done": True})
46
+ except WebSocketDisconnect:
47
+ pass
48
+
49
+
50
+ @app.get("/health")
51
+ def health():
52
+ return {"status": "ok"}