Files
LocalAI/core/services/studio/connectorturn.go
T
Ettore Di Giacinto a1157fb811 Tell the sender when an answer shows a file that was not attached
A model could write a paperclip line with a made-up file name and
never call attach_file. Nothing was sent and the sender got no note.

When the answer of a connector turn has a paperclip followed by the
name of a sendable file, and no file went and no other note was
sent, the host now adds one plain note. The answer is not changed.
Prose that names a file and a paperclip alone do not match.

The attach_file description and the connector guidance now say to
use the exact path the tools showed, and never to write a file name
or a paperclip line by hand. A spec covers a folder name with a
space.

Assisted-by: Claude:claude-sonnet-5-5
2026-11-04 09:14:18 +00:00

56 lines
2.4 KiB
Go

package studio
import (
"context"
"fmt"
"strings"
"github.com/mudler/LocalAI/core/services/studio/session"
)
// connectorBlock tells the model of a chat that a messaging app started where
// its answer goes. The person reads it there, not in Studio, and the host
// attaches the files a turn makes (sendFiles) and the files the model marks
// with attach_file, so the model must not say it cannot send a file or quote a
// path of the sandbox. It is a block of the turn
// prompt, not a note: a note is cut at 400 bytes, and the text of a block is
// the same at each turn, so the harness is not restarted and the transcript
// does not grow. Other chats get nothing. canWrite says whether the chat can
// make a file at all.
func (s *Service) connectorBlock(ctx context.Context, rec session.ChatRecord, canWrite bool) string {
if rec.Origin != session.OriginConnector {
return ""
}
platform := "a messaging app"
sends, known := false, false
var limits FileLimits
if c := s.connectors; c != nil {
if b, err := c.o.Store.GetBinding(ctx, rec.OriginID); err == nil {
limits = c.FileLimits(ctx, b)
sends, known = b.SendsFiles() && limits.Send.Bytes > 0, true
platform = b.Kind
if kinds, err := c.o.Transport.Kinds(ctx); err == nil {
if k, ok := kindFor(kinds, b); ok && k.Title != "" {
platform = k.Title
}
}
}
}
var sb strings.Builder
fmt.Fprintf(&sb, "This chat is on %s. The person reads your answer there, not in Studio, and cannot open sandbox paths.", platform)
switch {
case !canWrite:
case !known:
sb.WriteString(" Never tell them a file path.")
case !sends:
sb.WriteString(" Files are NOT sent to them: paste short content in the answer, or say you cannot attach a file. Never tell them a file path.")
default:
fmt.Fprintf(&sb, " Files you make or change in a turn are attached to your answer automatically. To give them a file that already exists, call %s with its path."+
" Use the exact path your file tools or bash showed, for a file in a folder you were given too."+
" Say the file is attached only after the call worked; never write a file name or a paperclip line yourself, never say you cannot send files, and never tell them a path."+
" Up to %d files per turn, %s each; documents, data, images, audio, video and zip only, no scripts or HTML. If one cannot be attached, say so.",
attachFileName, maxFilesPerTurn, limits.Send.sentence(limits.KindTitle))
}
return sb.String()
}