Skip to content

Commit 90b73e0

Browse files
metiu1claude
andcommitted
v0.3.57: created documents are findable + shown in the GUI
- create_document saves a bare filename to the user's Downloads folder (not the server's working dir) and returns the absolute path, so the file is findable. - The agentic gate emits a media_generated event for created documents, and the GUI renders a download card (with inline PDF preview), so the user actually gets the file instead of just being told it was created. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 02e35ca commit 90b73e0

8 files changed

Lines changed: 68 additions & 8 deletions

File tree

vortelio-pip/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "vortelio"
7-
version = "0.3.56"
7+
version = "0.3.57"
88
description = "Local-first AI platform. Run LLMs, generate images & video, transcribe audio, create 3D — on your own machine. OpenAI & Ollama API compatible. Apache 2.0."
99
readme = "README.md"
1010
requires-python = ">=3.8"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.3.56"
1+
__version__ = "0.3.57"

vortelio-pip/src/vortelio_cli/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import urllib.request
1111
from pathlib import Path
1212

13-
VERSION = "0.3.56"
13+
VERSION = "0.3.57"
1414
RELEASE_BASE = os.environ.get(
1515
"VORTELIO_RELEASE_BASE",
1616
f"https://github.com/metiu1/Vortelio/releases/download/v{VERSION}",
3 KB
Binary file not shown.

vortelio/internal/runtime/tools.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,19 +309,31 @@ func toolCreateDocument(argsJSON string) (string, error) {
309309
if strings.TrimSpace(a.Path) == "" {
310310
return "", fmt.Errorf("path is required")
311311
}
312+
// A bare filename (no directory) is saved to the user's Downloads folder so
313+
// the document is actually findable, instead of the server's working dir.
314+
if filepath.Dir(a.Path) == "." {
315+
a.Path = filepath.Join(DefaultOutputDir(), a.Path)
316+
}
312317
if dir := filepath.Dir(a.Path); dir != "" {
313318
os.MkdirAll(dir, 0755)
314319
}
315320
p, err := CreateDocument(a.Format, a.Path, a.Title, a.Content)
316321
if err != nil {
317322
return "", err
318323
}
324+
if abs, e := filepath.Abs(p); e == nil {
325+
p = abs
326+
}
319327
fi, _ := os.Stat(p)
320328
size := int64(0)
321329
if fi != nil {
322330
size = fi.Size()
323331
}
324-
return fmt.Sprintf("Documento creato: %s (%d byte)", p, size), nil
332+
b, _ := json.Marshal(map[string]interface{}{
333+
"status": "ok", "path": p, "size": size,
334+
"note": "Document saved. The user can open/download it from the path above.",
335+
})
336+
return string(b), nil
325337
}
326338

327339
// ── run_code ─────────────────────────────────────────────────────────────────

vortelio/internal/server/server_agentic.go

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package server
22

33
import (
4+
"encoding/base64"
45
"encoding/json"
56
"fmt"
67
"net/http"
@@ -509,7 +510,49 @@ func (g *gatedProvider) Execute(name, args string) (string, error) {
509510
}
510511
}
511512
}
512-
return g.inner.Execute(name, args)
513+
res, err := g.inner.Execute(name, args)
514+
// Surface a created document in the GUI like generated media (path + inline
515+
// download), otherwise the user is told "created" but never sees the file.
516+
if err == nil && name == "create_document" && g.emit != nil {
517+
g.emitDocument(res)
518+
}
519+
return res, err
520+
}
521+
522+
// emitDocument reads the just-created document and emits a media_generated event
523+
// so the GUI shows a clickable/downloadable card.
524+
func (g *gatedProvider) emitDocument(result string) {
525+
var r struct {
526+
Path string `json:"path"`
527+
}
528+
if json.Unmarshal([]byte(result), &r) != nil || r.Path == "" {
529+
return
530+
}
531+
data, err := os.ReadFile(r.Path)
532+
if err != nil || len(data) > 25*1024*1024 {
533+
// Still tell the UI where it is, even if too big to inline.
534+
g.emit("media_generated", map[string]interface{}{"kind": "document", "path": r.Path})
535+
return
536+
}
537+
mime := "application/octet-stream"
538+
switch strings.ToLower(filepath.Ext(r.Path)) {
539+
case ".pdf":
540+
mime = "application/pdf"
541+
case ".docx":
542+
mime = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
543+
case ".txt", ".md":
544+
mime = "text/plain"
545+
case ".html":
546+
mime = "text/html"
547+
case ".csv":
548+
mime = "text/csv"
549+
case ".json":
550+
mime = "application/json"
551+
}
552+
g.emit("media_generated", map[string]interface{}{
553+
"kind": "document", "path": r.Path, "mime": mime,
554+
"data_uri": "data:" + mime + ";base64," + base64.StdEncoding.EncodeToString(data),
555+
})
513556
}
514557

515558
func gatedSummary(name, args string) string {

vortelio/internal/server/ui.html

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3936,14 +3936,19 @@ <h3 style="margin:0">☁️ Add cloud model</h3>
39363936
function mediaArtifactEl(m) {
39373937
const wrap = document.createElement('div');
39383938
wrap.className = 'media-artifact';
3939-
const cap = '<div class="media-cap">🎨 ' + esc((m.kind||'media').toUpperCase()) + ' generated' +
3939+
const cap = '<div class="media-cap">' + (m.kind === 'document' ? '📄 ' : '🎨 ') + esc((m.kind||'media').toUpperCase()) + ' generated' +
39403940
(m.path ? ' · <span style="font-family:var(--mono);font-size:11px">' + esc(m.path) + '</span>' : '') + '</div>';
39413941
let body = '';
39423942
const src = m.data_uri || m.path || '';
39433943
if (m.kind === 'image') body = '<img src="' + src + '" style="max-width:100%;border-radius:8px">';
39443944
else if (m.kind === 'audio') body = '<audio controls src="' + src + '" style="width:100%"></audio>';
39453945
else if (m.kind === 'video') body = '<video controls src="' + src + '" style="max-width:100%;border-radius:8px"></video>';
3946-
else body = '<div style="font-size:12px;color:var(--text2)">File 3D salvato. Aprilo dalla cartella output.</div>';
3946+
else if (m.kind === 'document') {
3947+
const fn = String(m.path || 'document').split(/[\\/]/).pop();
3948+
body = '<a class="btn primary" href="' + src + '" download="' + esc(fn) + '" style="display:inline-flex;text-decoration:none">⬇ Scarica ' + esc(fn) + '</a>';
3949+
if (m.mime === 'application/pdf' && m.data_uri) body += '<embed src="' + src + '" type="application/pdf" style="width:100%;height:480px;border-radius:8px;margin-top:8px">';
3950+
}
3951+
else body = '<div style="font-size:12px;color:var(--text2)">File salvato. Percorso sopra.</div>';
39473952
wrap.innerHTML = cap + body;
39483953
return wrap;
39493954
}

vortelio/internal/version/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ package version
33
// Version è impostabile a build time via:
44
//
55
// -ldflags "-X github.com/vortelio/vortelio/internal/version.Version=X.Y.Z"
6-
var Version = "0.3.56"
6+
var Version = "0.3.57"

0 commit comments

Comments
 (0)