Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5870b5eede | ||
|
|
5c06b1a342 | ||
|
|
36a7e0281d | ||
|
|
d188ce5c63 |
24
.gitattributes
vendored
Normal file
24
.gitattributes
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
# Force LF for scripts (importante per VPS Linux)
|
||||||
|
*.sh text eol=lf
|
||||||
|
*.bash text eol=lf
|
||||||
|
deploy.sh text eol=lf
|
||||||
|
|
||||||
|
# Force LF for config files
|
||||||
|
*.yml text eol=lf
|
||||||
|
*.yaml text eol=lf
|
||||||
|
*.json text eol=lf
|
||||||
|
*.md text eol=lf
|
||||||
|
|
||||||
|
# Binary files
|
||||||
|
*.png binary
|
||||||
|
*.jpg binary
|
||||||
|
*.jpeg binary
|
||||||
|
*.gif binary
|
||||||
|
*.ico binary
|
||||||
|
*.woff binary
|
||||||
|
*.woff2 binary
|
||||||
|
*.ttf binary
|
||||||
|
*.eot binary
|
||||||
1
.planning/agent-history.json
Normal file
1
.planning/agent-history.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":"1.0","max_entries":50,"entries":[]}
|
||||||
28
.vps-lab-config.json
Normal file
28
.vps-lab-config.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"type": "vps-lab",
|
||||||
|
"project_name": "postgenerator",
|
||||||
|
"slug": "postgenerator",
|
||||||
|
"created_at": "2026-03-07T12:17:48Z",
|
||||||
|
"gitea": {
|
||||||
|
"repo_url": "https://git.mlhub.it/Michele/postgenerator",
|
||||||
|
"clone_url": "https://git.mlhub.it/Michele/postgenerator.git"
|
||||||
|
},
|
||||||
|
"vps": {
|
||||||
|
"deployed": true,
|
||||||
|
"url": "https://lab.mlhub.it/postgenerator/",
|
||||||
|
"last_deploy": "2026-03-09T11:06:00Z",
|
||||||
|
"container": "lab-postgenerator-app",
|
||||||
|
"path": "/opt/lab-postgenerator/"
|
||||||
|
},
|
||||||
|
"supabase": {
|
||||||
|
"enabled": false,
|
||||||
|
"project_ref": null
|
||||||
|
},
|
||||||
|
"resources": {
|
||||||
|
"limits": {
|
||||||
|
"ram_mb": 1024,
|
||||||
|
"cpu_percent": 100
|
||||||
|
},
|
||||||
|
"deployed_at": "2026-03-09T11:06:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
38
README.md
Normal file
38
README.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# postgenerator
|
||||||
|
|
||||||
|
Lab project hosted at https://lab.mlhub.it/postgenerator/
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
This project uses [gsd (get-shit-done)](https://github.com/glittercowboy/get-shit-done) for spec-driven development.
|
||||||
|
|
||||||
|
### Getting Started
|
||||||
|
|
||||||
|
1. Run `/gsd:new-project` to initialize project specs
|
||||||
|
2. Follow gsd workflow: discuss > plan > execute > verify
|
||||||
|
3. Run `vps-lab-deploy` when ready to deploy
|
||||||
|
|
||||||
|
### Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
postgenerator/
|
||||||
|
├── .planning/ # gsd planning docs (created by /gsd:new-project)
|
||||||
|
│ ├── PROJECT.md # Project vision
|
||||||
|
│ ├── REQUIREMENTS.md # Scoped requirements
|
||||||
|
│ ├── ROADMAP.md # Phase breakdown
|
||||||
|
│ └── STATE.md # Project memory
|
||||||
|
├── src/ # Source code (created during development)
|
||||||
|
└── README.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
## Repository
|
||||||
|
|
||||||
|
- **Gitea**: https://git.mlhub.it/Michele/postgenerator
|
||||||
|
- **Clone**: `git clone https://git.mlhub.it/Michele/postgenerator.git`
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
When ready to deploy, use `vps-lab-deploy` skill.
|
||||||
|
|
||||||
|
- **URL**: https://lab.mlhub.it/postgenerator/
|
||||||
|
- **VPS**: /opt/lab-postgenerator/
|
||||||
@@ -12,6 +12,7 @@ from __future__ import annotations
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
from typing import Type, TypeVar
|
from typing import Type, TypeVar
|
||||||
|
|
||||||
@@ -111,9 +112,10 @@ class LLMService:
|
|||||||
elapsed,
|
elapsed,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Valida con Pydantic
|
# Rimuovi eventuali code fences markdown e valida con Pydantic
|
||||||
|
clean_text = self._strip_code_fences(raw_text)
|
||||||
try:
|
try:
|
||||||
result = response_schema.model_validate_json(raw_text)
|
result = response_schema.model_validate_json(clean_text)
|
||||||
# Pausa inter-request dopo chiamata riuscita
|
# Pausa inter-request dopo chiamata riuscita
|
||||||
time.sleep(self._inter_request_delay)
|
time.sleep(self._inter_request_delay)
|
||||||
return result
|
return result
|
||||||
@@ -259,6 +261,20 @@ class LLMService:
|
|||||||
# Metodi privati
|
# Metodi privati
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _strip_code_fences(text: str) -> str:
|
||||||
|
"""Rimuove i code fences markdown dalla risposta LLM.
|
||||||
|
|
||||||
|
Claude a volte wrappa il JSON in ```json ... ``` anche quando
|
||||||
|
gli si chiede di rispondere solo con JSON.
|
||||||
|
"""
|
||||||
|
stripped = text.strip()
|
||||||
|
# Rimuove ```json ... ``` o ``` ... ```
|
||||||
|
match = re.match(r"^```(?:json)?\s*\n?(.*?)\n?\s*```$", stripped, re.DOTALL)
|
||||||
|
if match:
|
||||||
|
return match.group(1).strip()
|
||||||
|
return stripped
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parse_retry_after(error: anthropic.RateLimitError) -> float:
|
def _parse_retry_after(error: anthropic.RateLimitError) -> float:
|
||||||
"""Estrae il valore retry-after dall'eccezione RateLimitError.
|
"""Estrae il valore retry-after dall'eccezione RateLimitError.
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import type {
|
|||||||
export function useSettings() {
|
export function useSettings() {
|
||||||
return useQuery<Settings>({
|
return useQuery<Settings>({
|
||||||
queryKey: ['settings'],
|
queryKey: ['settings'],
|
||||||
queryFn: () => apiGet<Settings>('/settings'),
|
queryFn: () => apiGet<Settings>('/settings/'),
|
||||||
staleTime: 60_000,
|
staleTime: 60_000,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ export function useSettingsStatus() {
|
|||||||
export function useUpdateSettings() {
|
export function useUpdateSettings() {
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
return useMutation<Settings, Error, Partial<Settings>>({
|
return useMutation<Settings, Error, Partial<Settings>>({
|
||||||
mutationFn: (settings) => apiPut<Settings>('/settings', settings),
|
mutationFn: (settings) => apiPut<Settings>('/settings/', settings),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ['settings'] })
|
queryClient.invalidateQueries({ queryKey: ['settings'] })
|
||||||
},
|
},
|
||||||
@@ -199,7 +199,7 @@ export function useDownloadEditedCsv() {
|
|||||||
export function usePromptList() {
|
export function usePromptList() {
|
||||||
return useQuery<PromptListResponse>({
|
return useQuery<PromptListResponse>({
|
||||||
queryKey: ['prompts'],
|
queryKey: ['prompts'],
|
||||||
queryFn: () => apiGet<PromptListResponse>('/prompts'),
|
queryFn: () => apiGet<PromptListResponse>('/prompts/'),
|
||||||
staleTime: 30_000,
|
staleTime: 30_000,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user