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.
- {fraq-0.2.2 → fraq-0.2.4}/CHANGELOG.md +51 -0
- {fraq-0.2.2 → fraq-0.2.4}/PKG-INFO +6 -2
- {fraq-0.2.2 → fraq-0.2.4}/README.md +5 -1
- fraq-0.2.4/examples/cli-docker/run.py +63 -0
- fraq-0.2.4/examples/fastapi-docker/main.py +86 -0
- fraq-0.2.4/examples/fastapi-docker/run.py +98 -0
- fraq-0.2.4/examples/fullstack-docker/api/main.py +38 -0
- fraq-0.2.4/examples/fullstack-docker/frontend/app.py +60 -0
- fraq-0.2.4/examples/fullstack-docker/run.py +76 -0
- fraq-0.2.4/examples/fullstack-docker/websocket/main.py +52 -0
- fraq-0.2.4/examples/websocket-docker/main.py +104 -0
- fraq-0.2.4/examples/websocket-docker/run.py +88 -0
- {fraq-0.2.2 → fraq-0.2.4}/fraq/__init__.py +1 -1
- {fraq-0.2.2 → fraq-0.2.4}/fraq/cli.py +27 -21
- {fraq-0.2.2 → fraq-0.2.4}/fraq/text2fraq.py +193 -277
- {fraq-0.2.2 → fraq-0.2.4}/fraq.egg-info/SOURCES.txt +9 -0
- {fraq-0.2.2 → fraq-0.2.4}/pyproject.toml +1 -1
- {fraq-0.2.2 → fraq-0.2.4}/tests/test_text2fraq.py +78 -1
- {fraq-0.2.2 → fraq-0.2.4}/LICENSE +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/MANIFEST.in +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/examples/api_server.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/examples/app_integrations.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/examples/applications.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/examples/async_streaming.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/examples/nlp2cmd_integration.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/examples/query_examples.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/examples/text2fraq_examples.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/examples/text2fraq_files.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/fraq/adapters.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/fraq/core.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/fraq/formats.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/fraq/generators.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/fraq/py.typed +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/fraq/query.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/fraq/schema_export.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/fraq/streaming.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/setup.cfg +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/tests/__init__.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/tests/test_adapters.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/tests/test_cli.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/tests/test_core.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/tests/test_formats.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/tests/test_generators.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/tests/test_query.py +0 -0
- {fraq-0.2.2 → fraq-0.2.4}/tests/test_schema_export.py +0 -0
- {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.
|
|
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"}
|