Compare commits
No commits in common. "77c0d1bce8a28f0e575b0e17fc4301b78497c240" and "db788760a3cb386560b3f8be2a5339a191e00091" have entirely different histories.
77c0d1bce8
...
db788760a3
20
README.md
20
README.md
@ -4,25 +4,29 @@
|
||||
|
||||
Current features:
|
||||
- Perform one-shot prompts with `lmcli prompt <message>`
|
||||
- Manage persistent conversations with the `new`, `reply`, `view`, and `rm`,
|
||||
`edit`, `retry`, `continue` sub-commands.
|
||||
- Manage persistent conversations with the `new`, `reply`, `view`, and `rm`
|
||||
sub-commands.
|
||||
- Syntax highlighted output
|
||||
- Tool calling, see the [Tools](#tools) section.
|
||||
|
||||
Planned features:
|
||||
- Ask questions about content received on stdin
|
||||
- Conversation editing
|
||||
|
||||
Maybe features:
|
||||
- Chat-like interface (`lmcli chat`) for rapid back-and-forth conversations
|
||||
- Support for additional models/APIs besides just OpenAI
|
||||
- Natural language image generation, iterative editing
|
||||
|
||||
## Tools
|
||||
Tools must be explicitly enabled by adding the tool's name to the
|
||||
`openai.enabledTools` array in `config.yaml`.
|
||||
|
||||
Note: all filesystem related tools operate relative to the current directory
|
||||
only. They do not accept absolute paths, and efforts are made to ensure they
|
||||
cannot escape above the working directory). **Close attention must be paid to
|
||||
where you are running `lmcli`, as the model could at any time decide to use one
|
||||
of these tools to discover and read potentially sensitive information from your
|
||||
filesystem.**
|
||||
only. They do not accept absolute paths, and all efforts are made to ensure
|
||||
they cannot escape above the working directory (not quite using chroot, but in
|
||||
effect). **Close attention must be paid to where you are running `lmcli`, as
|
||||
the model could at any time decide to use one of these tools to discover and
|
||||
read potentially sensitive information from your filesystem.**
|
||||
|
||||
It's best to only have tools enabled in `config.yaml` when you intend to be
|
||||
using them, since their descriptions (see `pkg/cli/functions.go`) count towards
|
||||
|
@ -18,7 +18,7 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
// Limit number of conversations shown with `ls`, without --all
|
||||
// Limit to number of conversations shown with `ls`, without --all
|
||||
LS_LIMIT int = 25
|
||||
)
|
||||
|
||||
@ -32,15 +32,14 @@ func init() {
|
||||
cmd.MarkFlagsMutuallyExclusive("system-prompt", "system-prompt-file")
|
||||
}
|
||||
|
||||
listCmd.Flags().Bool("all", false, fmt.Sprintf("Show all conversations, by default only the last %d are shown", LS_LIMIT))
|
||||
lsCmd.Flags().Bool("all", false, fmt.Sprintf("Show all conversations, by default only the last %d are shown", LS_LIMIT))
|
||||
renameCmd.Flags().Bool("generate", false, "Generate a conversation title")
|
||||
editCmd.Flags().Int("offset", 1, "Offset from the last reply to edit (Default: edit your last message, assuming there's an assistant reply)")
|
||||
|
||||
rootCmd.AddCommand(
|
||||
cloneCmd,
|
||||
continueCmd,
|
||||
editCmd,
|
||||
listCmd,
|
||||
lsCmd,
|
||||
newCmd,
|
||||
promptCmd,
|
||||
renameCmd,
|
||||
@ -115,7 +114,8 @@ func lookupConversationE(shortName string) (*Conversation, error) {
|
||||
}
|
||||
|
||||
// handleConversationReply handles sending messages to an existing
|
||||
// conversation, optionally persisting both the sent replies and responses.
|
||||
// conversation, optionally persisting them. It displays the entire
|
||||
// conversation before
|
||||
func handleConversationReply(c *Conversation, persist bool, toSend ...Message) {
|
||||
existing, err := store.Messages(c)
|
||||
if err != nil {
|
||||
@ -182,15 +182,14 @@ var rootCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var listCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Aliases: []string{"ls"},
|
||||
var lsCmd = &cobra.Command{
|
||||
Use: "ls",
|
||||
Short: "List conversations",
|
||||
Long: `List conversations in order of recent activity`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
conversations, err := store.Conversations()
|
||||
if err != nil {
|
||||
Fatal("Could not fetch conversations.\n")
|
||||
fmt.Println("Could not fetch conversations.")
|
||||
return
|
||||
}
|
||||
|
||||
@ -660,39 +659,34 @@ var editCmd = &cobra.Command{
|
||||
Fatal("Could not retrieve messages for conversation: %s\n", conversation.Title)
|
||||
}
|
||||
|
||||
offset, _ := cmd.Flags().GetInt("offset")
|
||||
if offset < 0 {
|
||||
offset = -offset
|
||||
}
|
||||
|
||||
if offset > len(messages) - 1 {
|
||||
Fatal("Offset %d is before the start of the conversation\n", offset)
|
||||
}
|
||||
|
||||
desiredIdx := len(messages) - 1 - offset
|
||||
|
||||
// walk backwards through the conversation deleting messages until and
|
||||
// including the last user message
|
||||
toRemove := []Message{}
|
||||
var toEdit *Message
|
||||
var lastUserMessage *Message
|
||||
for i := len(messages) - 1; i >= 0; i-- {
|
||||
if i == desiredIdx {
|
||||
toEdit = &messages[i]
|
||||
if messages[i].Role == MessageRoleUser {
|
||||
lastUserMessage = &messages[i]
|
||||
}
|
||||
|
||||
toRemove = append(toRemove, messages[i])
|
||||
messages = messages[:i]
|
||||
if toEdit != nil {
|
||||
if lastUserMessage != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
existingContents := toEdit.OriginalContent
|
||||
if lastUserMessage == nil {
|
||||
Fatal("No messages left in the conversation, nothing to edit.\n")
|
||||
}
|
||||
|
||||
existingContents := lastUserMessage.OriginalContent
|
||||
|
||||
newContents := inputFromArgsOrEditor(args[1:], "# Save when finished editing\n", existingContents)
|
||||
switch newContents {
|
||||
case existingContents:
|
||||
if newContents == existingContents {
|
||||
Fatal("No edits were made.\n")
|
||||
case "":
|
||||
}
|
||||
|
||||
if newContents == "" {
|
||||
Fatal("No message was provided.\n")
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user