Compare commits

..

1 Commits
v0.0.11 ... ls

Author SHA1 Message Date
Patrick Devine
b0b7641b80 add ls alias 2023-07-20 15:26:33 -07:00
9 changed files with 51 additions and 212 deletions

View File

@@ -125,13 +125,3 @@ Finally, run a model!
```
./ollama run llama2
```
## REST API
### `POST /api/generate`
Generate text from a model.
```
curl -X POST http://localhost:11434/api/generate -d '{"model": "llama2", "prompt":"Why is the sky blue?"}'
```

View File

@@ -27,7 +27,7 @@ func checkError(resp *http.Response, body []byte) error {
err := json.Unmarshal(body, &apiError)
if err != nil {
// Use the full body as the message if we fail to decode a response.
apiError.ErrorMessage = string(body)
apiError.Message = string(body)
}
return apiError
@@ -92,6 +92,7 @@ func (c *Client) do(ctx context.Context, method, path string, reqData, respData
}
}
return nil
}
func (c *Client) stream(ctx context.Context, method, path string, data any, fn func([]byte) error) error {
@@ -136,9 +137,9 @@ func (c *Client) stream(ctx context.Context, method, path string, data any, fn f
if response.StatusCode >= 400 {
return StatusError{
StatusCode: response.StatusCode,
Status: response.Status,
ErrorMessage: errorResponse.Error,
StatusCode: response.StatusCode,
Status: response.Status,
Message: errorResponse.Error,
}
}
@@ -209,16 +210,3 @@ func (c *Client) List(ctx context.Context) (*ListResponse, error) {
}
return &lr, nil
}
type DeleteProgressFunc func(ProgressResponse) error
func (c *Client) Delete(ctx context.Context, req *DeleteRequest, fn DeleteProgressFunc) error {
return c.stream(ctx, http.MethodDelete, "/api/delete", req, func(bts []byte) error {
var resp ProgressResponse
if err := json.Unmarshal(bts, &resp); err != nil {
return err
}
return fn(resp)
})
}

View File

@@ -8,23 +8,16 @@ import (
)
type StatusError struct {
StatusCode int
Status string
ErrorMessage string `json:"error"`
StatusCode int
Status string
Message string
}
func (e StatusError) Error() string {
switch {
case e.Status != "" && e.ErrorMessage != "":
return fmt.Sprintf("%s: %s", e.Status, e.ErrorMessage)
case e.Status != "":
return e.Status
case e.ErrorMessage != "":
return e.ErrorMessage
default:
// this should not happen
return "something went wrong, please see the ollama server logs for details"
if e.Message != "" {
return fmt.Sprintf("%s: %s", e.Status, e.Message)
}
return e.Status
}
type GenerateRequest struct {
@@ -44,10 +37,6 @@ type CreateProgress struct {
Status string `json:"status"`
}
type DeleteRequest struct {
Name string `json:"name"`
}
type PullRequest struct {
Name string `json:"name"`
Username string `json:"username"`
@@ -55,10 +44,10 @@ type PullRequest struct {
}
type ProgressResponse struct {
Status string `json:"status"`
Digest string `json:"digest,omitempty"`
Total int `json:"total,omitempty"`
Completed int `json:"completed,omitempty"`
Status string `json:"status"`
Digest string `json:"digest,omitempty"`
Total int `json:"total,omitempty"`
Completed int `json:"completed,omitempty"`
}
type PushRequest struct {

View File

@@ -25,7 +25,7 @@ import (
"github.com/jmorganca/ollama/server"
)
func CreateHandler(cmd *cobra.Command, args []string) error {
func create(cmd *cobra.Command, args []string) error {
filename, _ := cmd.Flags().GetString("file")
filename, err := filepath.Abs(filename)
if err != nil {
@@ -59,7 +59,7 @@ func CreateHandler(cmd *cobra.Command, args []string) error {
return nil
}
func RunHandler(cmd *cobra.Command, args []string) error {
func RunRun(cmd *cobra.Command, args []string) error {
mp := server.ParseModelPath(args[0])
fp, err := mp.GetManifestPath(false)
if err != nil {
@@ -86,7 +86,7 @@ func RunHandler(cmd *cobra.Command, args []string) error {
return RunGenerate(cmd, args)
}
func PushHandler(cmd *cobra.Command, args []string) error {
func push(cmd *cobra.Command, args []string) error {
client := api.NewClient()
request := api.PushRequest{Name: args[0]}
@@ -101,7 +101,7 @@ func PushHandler(cmd *cobra.Command, args []string) error {
return nil
}
func ListHandler(cmd *cobra.Command, args []string) error {
func list(cmd *cobra.Command, args []string) error {
client := api.NewClient()
models, err := client.List(context.Background())
@@ -131,22 +131,7 @@ func ListHandler(cmd *cobra.Command, args []string) error {
return nil
}
func DeleteHandler(cmd *cobra.Command, args []string) error {
client := api.NewClient()
request := api.DeleteRequest{Name: args[0]}
fn := func(resp api.ProgressResponse) error {
fmt.Println(resp.Status)
return nil
}
if err := client.Delete(context.Background(), &request, fn); err != nil {
return err
}
return nil
}
func PullHandler(cmd *cobra.Command, args []string) error {
func RunPull(cmd *cobra.Command, args []string) error {
return pull(args[0])
}
@@ -305,7 +290,7 @@ func generateInteractive(cmd *cobra.Command, model string) error {
switch {
case strings.HasPrefix(line, "/list"):
args := strings.Fields(line)
if err := ListHandler(cmd, args[1:]); err != nil {
if err := list(cmd, args[1:]); err != nil {
return err
}
@@ -402,7 +387,7 @@ func NewCLI() *cobra.Command {
Use: "create MODEL",
Short: "Create a model from a Modelfile",
Args: cobra.MinimumNArgs(1),
RunE: CreateHandler,
RunE: create,
}
createCmd.Flags().StringP("file", "f", "Modelfile", "Name of the Modelfile (default \"Modelfile\")")
@@ -411,7 +396,7 @@ func NewCLI() *cobra.Command {
Use: "run MODEL [PROMPT]",
Short: "Run a model",
Args: cobra.MinimumNArgs(1),
RunE: RunHandler,
RunE: RunRun,
}
runCmd.Flags().Bool("verbose", false, "Show timings for response")
@@ -427,28 +412,21 @@ func NewCLI() *cobra.Command {
Use: "pull MODEL",
Short: "Pull a model from a registry",
Args: cobra.MinimumNArgs(1),
RunE: PullHandler,
RunE: RunPull,
}
pushCmd := &cobra.Command{
Use: "push MODEL",
Short: "Push a model to a registry",
Args: cobra.MinimumNArgs(1),
RunE: PushHandler,
RunE: push,
}
listCmd := &cobra.Command{
Use: "list",
Aliases: []string{"ls"},
Short: "List models",
RunE: ListHandler,
}
deleteCmd := &cobra.Command{
Use: "rm",
Short: "Remove a model",
Args: cobra.MinimumNArgs(1),
RunE: DeleteHandler,
RunE: list,
}
rootCmd.AddCommand(
@@ -458,7 +436,6 @@ func NewCLI() *cobra.Command {
pullCmd,
pushCmd,
listCmd,
deleteCmd,
)
return rootCmd

View File

@@ -20,8 +20,14 @@ What the model file looks like:
```
FROM llama2
PARAMETER temperature 1
SYSTEM """
You are Mario from Super Mario Bros, acting as an assistant.
PROMPT """
{{- if not .Context }}
<<SYS>>
You are Mario from super mario bros, acting as an assistant.
<</SYS>>
{{- end }}
[INST] {{ .Prompt }} [/INST]
"""
```

View File

@@ -2,6 +2,6 @@
# Run `ollama create tweetwriter -f ./Modelfile` and then `ollama run tweetwriter` and enter a topic
FROM nous-hermes
SYSTEM """
PROMPT """
You are a content marketer who needs to come up with a short but succinct tweet. Make sure to include the appropriate hashtags and links. Sometimes when appropriate, describe a meme that can be includes as well. All answers should be in the form of a tweet which has a max size of 280 characters. Every instruction will be the topic to create a tweet about.
"""

View File

@@ -487,83 +487,6 @@ func CreateLayer(f io.ReadSeeker) (*LayerReader, error) {
return layer, nil
}
func DeleteModel(name string, fn func(api.ProgressResponse)) error {
mp := ParseModelPath(name)
manifest, err := GetManifest(mp)
if err != nil {
fn(api.ProgressResponse{Status: "couldn't retrieve manifest"})
return err
}
deleteMap := make(map[string]bool)
for _, layer := range manifest.Layers {
deleteMap[layer.Digest] = true
}
deleteMap[manifest.Config.Digest] = true
fp, err := GetManifestPath()
if err != nil {
fn(api.ProgressResponse{Status: "problem getting manifest path"})
return err
}
err = filepath.Walk(fp, func(path string, info os.FileInfo, err error) error {
if err != nil {
fn(api.ProgressResponse{Status: "problem walking manifest dir"})
return err
}
if !info.IsDir() {
path := path[len(fp)+1:]
slashIndex := strings.LastIndex(path, "/")
if slashIndex == -1 {
return nil
}
tag := path[:slashIndex] + ":" + path[slashIndex+1:]
fmp := ParseModelPath(tag)
// skip the manifest we're trying to delete
if mp.GetFullTagname() == fmp.GetFullTagname() {
return nil
}
// save (i.e. delete from the deleteMap) any files used in other manifests
manifest, err := GetManifest(fmp)
if err != nil {
log.Printf("skipping file: %s", fp)
return nil
}
for _, layer := range manifest.Layers {
delete(deleteMap, layer.Digest)
}
delete(deleteMap, manifest.Config.Digest)
}
return nil
})
// only delete the files which are still in the deleteMap
for k, v := range deleteMap {
if v {
err := os.Remove(k)
if err != nil {
log.Printf("couldn't remove file '%s': %v", k, err)
continue
}
}
}
fp, err = mp.GetManifestPath(false)
if err != nil {
return err
}
err = os.Remove(fp)
if err != nil {
log.Printf("couldn't remove manifest file '%s': %v", fp, err)
return err
}
fn(api.ProgressResponse{Status: fmt.Sprintf("deleted '%s'", name)})
return nil
}
func PushModel(name, username, password string, fn func(api.ProgressResponse)) error {
mp := ParseModelPath(name)

View File

@@ -2,7 +2,6 @@ package server
import (
"encoding/json"
"errors"
"io"
"log"
"net"
@@ -19,7 +18,7 @@ import (
"github.com/jmorganca/ollama/llama"
)
func GenerateHandler(c *gin.Context) {
func generate(c *gin.Context) {
start := time.Now()
var req api.GenerateRequest
@@ -79,7 +78,7 @@ func GenerateHandler(c *gin.Context) {
streamResponse(c, ch)
}
func PullModelHandler(c *gin.Context) {
func pull(c *gin.Context) {
var req api.PullRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
@@ -101,7 +100,7 @@ func PullModelHandler(c *gin.Context) {
streamResponse(c, ch)
}
func PushModelHandler(c *gin.Context) {
func push(c *gin.Context) {
var req api.PushRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
@@ -123,7 +122,7 @@ func PushModelHandler(c *gin.Context) {
streamResponse(c, ch)
}
func CreateModelHandler(c *gin.Context) {
func create(c *gin.Context) {
var req api.CreateRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})
@@ -147,30 +146,7 @@ func CreateModelHandler(c *gin.Context) {
streamResponse(c, ch)
}
func DeleteModelHandler(c *gin.Context) {
var req api.DeleteRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
ch := make(chan any)
go func() {
defer close(ch)
fn := func(r api.ProgressResponse) {
ch <- r
}
if err := DeleteModel(req.Name, fn); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
}()
streamResponse(c, ch)
}
func ListModelsHandler(c *gin.Context) {
func list(c *gin.Context) {
var models []api.ListResponseModel
fp, err := GetManifestPath()
if err != nil {
@@ -179,10 +155,6 @@ func ListModelsHandler(c *gin.Context) {
}
err = filepath.Walk(fp, func(path string, info os.FileInfo, err error) error {
if err != nil {
if errors.Is(err, os.ErrNotExist) {
log.Printf("manifest file does not exist: %s", fp)
return nil
}
return err
}
if !info.IsDir() {
@@ -227,12 +199,11 @@ func Serve(ln net.Listener) error {
c.String(http.StatusOK, "Ollama is running")
})
r.POST("/api/pull", PullModelHandler)
r.POST("/api/generate", GenerateHandler)
r.POST("/api/create", CreateModelHandler)
r.POST("/api/push", PushModelHandler)
r.GET("/api/tags", ListModelsHandler)
r.DELETE("/api/delete", DeleteModelHandler)
r.POST("/api/pull", pull)
r.POST("/api/generate", generate)
r.POST("/api/create", create)
r.POST("/api/push", push)
r.GET("/api/tags", list)
log.Printf("Listening on %s", ln.Addr())
s := &http.Server{

View File

@@ -11,23 +11,18 @@ export default async function Home() {
<Image src='/ollama.png' width={64} height={64} alt='ollamaIcon' />
<section className='my-12 text-center'>
<div className='flex flex-col space-y-2'>
<h2 className='md:max-w-md mx-auto my-2 text-3xl tracking-tight'>
Get up and running with large language models, locally.
</h2>
<h2 className='md:max-w-[18rem] mx-auto my-2 text-3xl tracking-tight'>Portable large language models</h2>
<h3 className='md:max-w-xs mx-auto text-base text-neutral-500'>
Run Llama 2 and other models on macOS. Customize and create your own.
Bundle a models weights, configuration, prompts, data and more into self-contained packages that run anywhere.
</h3>
</div>
<div className='mx-auto max-w-xs flex flex-col space-y-4 mt-12'>
<Link
href='/download'
className='md:mx-10 lg:mx-14 bg-black text-white rounded-full px-4 py-2 focus:outline-none cursor-pointer'
>
<div className='mx-auto flex flex-col space-y-4 mt-12'>
<Link href='/download' className='md:mx-10 lg:mx-14 bg-black text-white rounded-full px-4 py-2 focus:outline-none cursor-pointer'>
Download
</Link>
<p className='text-neutral-500 text-sm '>
Available for macOS with Apple Silicon <br />
Windows & Linux support coming soon.
Available for macOS with Apple Silicon <br />
Windows & Linux support coming soon.
</p>
</div>
</section>