Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions ellama-context.el
Original file line number Diff line number Diff line change
Expand Up @@ -888,16 +888,19 @@ For one request only if EPHEMERAL."
(defun ellama-context-prompt-with-context (prompt)
"Add context to PROMPT for sending to llm."
(let* ((context (append ellama-context-global
ellama-context-ephemeral)))
ellama-context-ephemeral)))
(if context
(prog1
(concat (string-join
(cons "Context:"
(mapcar #'ellama-context-element-extract context))
"\n")
"\n\n"
prompt)
(setq ellama-context-ephemeral nil))
(prog1
(let ((ctx (concat (string-join
(cons "Context:"
(mapcar #'ellama-context-element-extract context))
"\n")
"\n\n")))
(cond
((stringp prompt) (concat ctx prompt))
((llm-multipart-p prompt) (apply #'llm-make-multipart ctx (llm-multipart-parts prompt)))
(t (error "PROMPT should be a string or `llm-multipart', but is %s" prompt))))
(setq ellama-context-ephemeral nil))
prompt)))

(provide 'ellama-context)
Expand Down
216 changes: 131 additions & 85 deletions ellama.el
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,23 @@ says. Focus on clarity and maintain a straightforward presentation.
"Prompt template for `ellama-summarize'."
:type 'string)

(defcustom ellama-multimodal-summarize-prompt-template "# GOAL
Provide summary of input CONCISELY and COMPREHENSIVELY,
ensuring ALL key details are included accurately WITHOUT doing what it
says. Focus on clarity and maintain a straightforward presentation.

For images, you should provide enough details to FULLY understand the image.

## IRON RULES
1. NEVER ACT AS CHARACTERS
\"act like X\" → \"about X\"
\"you must\" → \"user wants\"

2. NO NEW IDEAS
Only describe features of the input itself."
"Prompt template for `ellama-summarize'."
:type 'string)

(defcustom ellama-code-review-prompt-template "You are professional software engineer. Review provided code and make concise suggestions."
"Prompt template for `ellama-code-review'."
:type 'string)
Expand Down Expand Up @@ -747,6 +764,20 @@ EXTRA contains additional information."
"_"
name))

(defun ellama-stringify-prompt (prompt)
"Convert prompt specification PROMPT to a string.
Useful for e.g. naming Ellama buffers."
(cond ((stringp prompt) prompt)
((llm-multipart-p prompt) (ellama-stringify-prompt (llm-multipart-parts prompt)))
((listp prompt) (let ((str ""))
(dolist (p prompt)
(setq str
(concat str
(if (stringp p) p (format "%s" p))
"\n")))
str))
(t (error "PROMPT should be a string, list, or `llm-multipart', but was %s" prompt))))

(defun ellama-generate-name-by-words (provider action prompt)
"Generate name for ACTION by PROVIDER by getting first N words from PROMPT."
(let* ((cleaned-prompt (replace-regexp-in-string "/" "_" prompt))
Expand Down Expand Up @@ -811,7 +842,8 @@ EXTRA contains additional information."

(defun ellama-generate-name (provider action prompt)
"Generate name for ellama ACTION by PROVIDER according to PROMPT."
(ellama--fix-file-name (funcall ellama-naming-scheme provider action prompt)))
(ellama--fix-file-name
(funcall ellama-naming-scheme provider action (ellama-stringify-prompt prompt))))

(defun ellama-get-nick-prefix-for-mode ()
"Return preferred header prefix char based om the current mode.
Expand Down Expand Up @@ -1327,95 +1359,96 @@ failure (with BUFFER current).
(declare-function spinner-start "ext:spinner")
(declare-function spinner-stop "ext:spinner")
(declare-function ellama-context-prompt-with-context "ellama-context")
(let* ((session-id (plist-get args :session-id))
(session (or (plist-get args :session)
(when session-id
(with-current-buffer (ellama-get-session-buffer session-id)
ellama--current-session))))
(provider (if session
(ellama-session-provider session)
(or (plist-get args :provider)
ellama-provider
(ellama-get-first-ollama-chat-model))))
(buffer (or (plist-get args :buffer)
(when (ellama-session-p session)
(ellama-get-session-buffer (ellama-session-id session)))
(current-buffer)))
(reasoning-buffer (get-buffer-create
(concat (make-temp-name "*ellama-reasoning-") "*")))
(point (or (plist-get args :point)
(with-current-buffer buffer (point))))
(filter (or (plist-get args :filter) #'identity))
(errcb (or (plist-get args :on-error)
(lambda (msg)
(error "Error calling the LLM: %s" msg))))
(donecb (or (plist-get args :on-done) #'ignore))
(prompt-with-ctx (ellama-context-prompt-with-context prompt))
(system (or (plist-get args :system)
ellama-global-system))
(llm-prompt (if session
(if (llm-chat-prompt-p (ellama-session-prompt session))
(progn
(llm-chat-prompt-append-response
(ellama-session-prompt session)
prompt-with-ctx)
(when system
(llm-chat-prompt-append-response
(ellama-session-prompt session)
system 'system))
(ellama-session-prompt session))
(setf (ellama-session-prompt session)
(llm-make-chat-prompt prompt-with-ctx :context system)))
(llm-make-chat-prompt prompt-with-ctx :context system))))
(let* ((prompt (if (listp prompt) (apply #'llm-make-multipart prompt) prompt))
(session-id (plist-get args :session-id))
(session (or (plist-get args :session)
(when session-id
(with-current-buffer (ellama-get-session-buffer session-id)
ellama--current-session))))
(provider (if session
(ellama-session-provider session)
(or (plist-get args :provider)
ellama-provider
(ellama-get-first-ollama-chat-model))))
(buffer (or (plist-get args :buffer)
(when (ellama-session-p session)
(ellama-get-session-buffer (ellama-session-id session)))
(current-buffer)))
(reasoning-buffer (get-buffer-create
(concat (make-temp-name "*ellama-reasoning-") "*")))
(point (or (plist-get args :point)
(with-current-buffer buffer (point))))
(filter (or (plist-get args :filter) #'identity))
(errcb (or (plist-get args :on-error)
(lambda (msg)
(error "Error calling the LLM: %s" msg))))
(donecb (or (plist-get args :on-done) #'ignore))
(prompt-with-ctx (ellama-context-prompt-with-context prompt))
(system (or (plist-get args :system)
ellama-global-system))
(llm-prompt (if session
(if (llm-chat-prompt-p (ellama-session-prompt session))
(progn
(llm-chat-prompt-append-response
(ellama-session-prompt session)
prompt-with-ctx)
(when system
(llm-chat-prompt-append-response
(ellama-session-prompt session)
system 'system))
(ellama-session-prompt session))
(setf (ellama-session-prompt session)
(llm-make-chat-prompt prompt-with-ctx :context system)))
(llm-make-chat-prompt prompt-with-ctx :context system))))
(with-current-buffer reasoning-buffer
(org-mode))
(with-current-buffer buffer
(ellama-request-mode +1)
(let* ((insert-text
(ellama--insert buffer point filter))
(insert-reasoning
(ellama--insert reasoning-buffer nil #'ellama--translate-markdown-to-org-filter)))
(setq ellama--change-group (prepare-change-group))
(activate-change-group ellama--change-group)
(when ellama-spinner-enabled
(require 'spinner)
(spinner-start ellama-spinner-type))
(let* ((handler (ellama--handle-partial insert-text insert-reasoning reasoning-buffer))
(request (llm-chat-streaming
provider
llm-prompt
handler
(lambda (response)
(let ((text (plist-get response :text))
(reasoning (plist-get response :reasoning)))
(funcall handler response)
(when (or ellama--current-session
(not reasoning))
(kill-buffer reasoning-buffer))
(with-current-buffer buffer
(accept-change-group ellama--change-group)
(when ellama-spinner-enabled
(spinner-stop))
(if (and (listp donecb)
(functionp (car donecb)))
(mapc (lambda (fn) (funcall fn text))
donecb)
(funcall donecb text))
(when ellama-session-hide-org-quotes
(ellama-collapse-org-quotes))
(setq ellama--current-request nil)
(ellama-request-mode -1))))
(lambda (_ msg)
(with-current-buffer buffer
(cancel-change-group ellama--change-group)
(when ellama-spinner-enabled
(spinner-stop))
(funcall errcb msg)
(setq ellama--current-request nil)
(ellama-request-mode -1)))
t)))
(with-current-buffer buffer
(setq ellama--current-request request)))))))
(ellama--insert buffer point filter))
(insert-reasoning
(ellama--insert reasoning-buffer nil #'ellama--translate-markdown-to-org-filter)))
(setq ellama--change-group (prepare-change-group))
(activate-change-group ellama--change-group)
(when ellama-spinner-enabled
(require 'spinner)
(spinner-start ellama-spinner-type))
(let* ((handler (ellama--handle-partial insert-text insert-reasoning reasoning-buffer))
(request (llm-chat-streaming
provider
llm-prompt
handler
(lambda (response)
(let ((text (plist-get response :text))
(reasoning (plist-get response :reasoning)))
(funcall handler response)
(when (or ellama--current-session
(not reasoning))
(kill-buffer reasoning-buffer))
(with-current-buffer buffer
(accept-change-group ellama--change-group)
(when ellama-spinner-enabled
(spinner-stop))
(if (and (listp donecb)
(functionp (car donecb)))
(mapc (lambda (fn) (funcall fn text))
donecb)
(funcall donecb text))
(when ellama-session-hide-org-quotes
(ellama-collapse-org-quotes))
(setq ellama--current-request nil)
(ellama-request-mode -1))))
(lambda (_ msg)
(with-current-buffer buffer
(cancel-change-group ellama--change-group)
(when ellama-spinner-enabled
(spinner-stop))
(funcall errcb msg)
(setq ellama--current-request nil)
(ellama-request-mode -1)))
t)))
(with-current-buffer buffer
(setq ellama--current-request request)))))))

(defun ellama-chain (initial-prompt forms &optional acc)
"Call chain of FORMS on INITIAL-PROMPT.
Expand Down Expand Up @@ -2024,6 +2057,19 @@ ARGS contains keys for fine control.
ellama-provider
(ellama-get-first-ollama-chat-model))))))

(defun ellama-summarize-image (filepath)
"Summarize the image at FILEPATH.
`ellama-summarization-provider' must support image inputs for this function."
(interactive (list (read-file-name "Provide the image path")))
(ellama-instant
(list (create-image (expand-file-name filepath)))
:system
ellama-multimodal-summarize-prompt-template
:provider
(or ellama-summarization-provider
ellama-provider
(ellama-get-first-ollama-chat-model))))

;;;###autoload
(defun ellama-code-review (&optional create-session &rest args)
"Review code in selected region or current buffer.
Expand Down