mirror of
https://github.com/mudler/LocalAI.git
synced 2026-11-06 12:33:16 -05:00
feat(addons): serve the op history in the shared connector route
A platform that implements HistoryReader answers the op history. The route checks the thread, bounds the limit and the answer, and does not wait out a rate limit. Assisted-by: Claude Code:claude-sonnet-5-5 Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
2 files changed
+79
No files matched your search
@@ -93,6 +93,14 @@ type Drafter interface {
|
||||
Draft(ctx context.Context, req contract.ConnectorRequest) error
|
||||
}
|
||||
|
||||
// HistoryReader is a Platform that reads the earlier messages of a thread for
|
||||
// the op history. It returns the messages oldest first, the newest of those
|
||||
// before req.Before, at most req.Limit of them, and whether older messages
|
||||
// exist. Serve checks the request and bounds the answer.
|
||||
type HistoryReader interface {
|
||||
History(ctx context.Context, req contract.ConnectorRequest) ([]contract.ConnectorHistoryMessage, bool, error)
|
||||
}
|
||||
|
||||
// Splitter is a Platform that cuts its texts itself; others use Split.
|
||||
type Splitter interface {
|
||||
Split(text string) []string
|
||||
@@ -159,6 +167,24 @@ func Serve(ctx context.Context, p Platform, req contract.ConnectorRequest) (cont
|
||||
if f, err = m.Media(ctx, req.Token, req.Media); err == nil {
|
||||
out.File = &f
|
||||
}
|
||||
case contract.ConnectorOpHistory:
|
||||
h, ok := p.(HistoryReader)
|
||||
if !ok || req.Chat == "" || req.Thread == "" {
|
||||
return contract.ConnectorResponse{}, fmt.Errorf("%w: no thread, or a platform that cannot read one", contract.ErrRefused)
|
||||
}
|
||||
if req.Limit < 1 || req.Limit > contract.MaxHistoryMessages {
|
||||
req.Limit = contract.DefaultHistoryMessages
|
||||
}
|
||||
// Not retried after a wait: the history is context, and the host
|
||||
// would rather answer without it than hold the answer behind it.
|
||||
var msgs []contract.ConnectorHistoryMessage
|
||||
var more bool
|
||||
if msgs, more, err = h.History(ctx, req); err == nil {
|
||||
if len(msgs) > req.Limit {
|
||||
msgs, more = msgs[len(msgs)-req.Limit:], true
|
||||
}
|
||||
out.Messages, out.Truncated = msgs, more
|
||||
}
|
||||
default:
|
||||
return contract.ConnectorResponse{}, fmt.Errorf("%w: unknown op", contract.ErrRefused)
|
||||
}
|
||||
|
||||
@@ -94,6 +94,19 @@ func (r *rich) Draft(_ context.Context, req contract.ConnectorRequest) error {
|
||||
return r.err
|
||||
}
|
||||
|
||||
// reading also reads the history of a thread.
|
||||
type reading struct {
|
||||
script
|
||||
msgs []contract.ConnectorHistoryMessage
|
||||
more bool
|
||||
got []contract.ConnectorRequest
|
||||
}
|
||||
|
||||
func (r *reading) History(_ context.Context, req contract.ConnectorRequest) ([]contract.ConnectorHistoryMessage, bool, error) {
|
||||
r.got = append(r.got, req)
|
||||
return r.msgs, r.more, r.err
|
||||
}
|
||||
|
||||
var _ = Describe("Serve", func() {
|
||||
ctx := context.Background()
|
||||
req := func(op string) contract.ConnectorRequest {
|
||||
@@ -287,4 +300,44 @@ var _ = Describe("Serve", func() {
|
||||
}
|
||||
Expect(f.files).To(HaveLen(1))
|
||||
})
|
||||
|
||||
Describe("the op history", func() {
|
||||
hist := func() contract.ConnectorRequest {
|
||||
r := req("history")
|
||||
r.Thread, r.Limit, r.Before = "t1", 2, "9"
|
||||
return r
|
||||
}
|
||||
It("is refused on a platform that cannot read a thread, and without a thread", func() {
|
||||
_, err := connector.Serve(ctx, &script{}, hist())
|
||||
Expect(err).To(MatchError(contract.ErrRefused))
|
||||
r := hist()
|
||||
r.Thread = ""
|
||||
_, err = connector.Serve(ctx, &reading{}, r)
|
||||
Expect(err).To(MatchError(contract.ErrRefused))
|
||||
})
|
||||
It("returns the newest messages within the limit and says that older ones exist", func() {
|
||||
s := &reading{msgs: []contract.ConnectorHistoryMessage{{Text: "a"}, {Text: "b"}, {Text: "c"}}}
|
||||
out, err := connector.Serve(ctx, s, hist())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(out.Messages).To(HaveLen(2))
|
||||
Expect(out.Messages[0].Text).To(Equal("b"))
|
||||
Expect(out.Truncated).To(BeTrue())
|
||||
Expect(s.got[0].Before).To(Equal("9"))
|
||||
})
|
||||
It("uses the default limit when the host asks for none or too many", func() {
|
||||
s := &reading{}
|
||||
r := hist()
|
||||
r.Limit = contract.MaxHistoryMessages + 1
|
||||
_, err := connector.Serve(ctx, s, r)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(s.got[0].Limit).To(Equal(contract.DefaultHistoryMessages))
|
||||
})
|
||||
It("types the error of the platform and does not wait for a rate limit", func() {
|
||||
s := &reading{script: script{err: &connector.RetryAfter{Seconds: 30}}}
|
||||
out, err := connector.Serve(ctx, s, hist())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(out.Error.Code).To(Equal(contract.ConnectorRateLimited))
|
||||
Expect(s.got).To(HaveLen(1))
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user