{"id":17681737,"url":"https://github.com/jgaskins/anthropic","last_synced_at":"2025-05-12T23:12:11.235Z","repository":{"id":252306109,"uuid":"829258071","full_name":"jgaskins/anthropic","owner":"jgaskins","description":"Client for the Claude AI models via the Anthropic API","archived":false,"fork":false,"pushed_at":"2025-02-24T23:26:03.000Z","size":120,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-06T18:16:16.395Z","etag":null,"topics":["anthropic","anthropic-ai","anthropic-api","anthropic-claude","claude","claude-ai"],"latest_commit_sha":null,"homepage":"https://jgaskins.dev/anthropic","language":"Crystal","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jgaskins.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-16T04:45:27.000Z","updated_at":"2025-04-15T20:39:48.000Z","dependencies_parsed_at":"2024-08-15T02:26:49.391Z","dependency_job_id":"e84b1541-f46a-4d4a-9661-bb57a7c97da0","html_url":"https://github.com/jgaskins/anthropic","commit_stats":null,"previous_names":["jgaskins/anthropic"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jgaskins%2Fanthropic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jgaskins%2Fanthropic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jgaskins%2Fanthropic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jgaskins%2Fanthropic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jgaskins","download_url":"https://codeload.github.com/jgaskins/anthropic/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253837459,"owners_count":21971984,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["anthropic","anthropic-ai","anthropic-api","anthropic-claude","claude","claude-ai"],"created_at":"2024-10-24T09:12:01.647Z","updated_at":"2025-05-12T23:12:11.213Z","avatar_url":"https://github.com/jgaskins.png","language":"Crystal","funding_links":[],"categories":[],"sub_categories":[],"readme":"# anthropic\n\nClient for the Anthropic API. Supports tool use and running those tools automatically.\n\n## Installation\n\n1. Add the dependency to your `shard.yml`:\n\n   ```yaml\n   dependencies:\n     anthropic:\n       github: jgaskins/anthropic\n   ```\n\n2. Run `shards install`\n\n## Usage\n\n```crystal\nrequire \"anthropic\"\n```\n\nThe main entrypoint is via the `Anthropic::Client`. You can instantiate it explicitly with an API key:\n\n```crystal\nclaude = Anthropic::Client.new(\"sk-and-api-03-asdfasdfasdf\")\n```\n\nOr you can omit the API key to automatically read it from the `ANTHROPIC_API_KEY` environment variable:\n\n```crystal\nclaude = Anthropic::Client.new\n```\n\nNext, use the `Anthropic::Messages#create` method to send your prompt:\n\n```crystal\nresponse = claude.messages.create(\n  # Pass a string representing the model name, or retrieve the full model\n  # name via the shorthand with Anthropic.model_name.\n  model: Anthropic.model_name(:sonnet),\n\n  # Define a system prompt if you want to give the AI a persona to use or some\n  # instructions on how to respond to the prompt.\n  system: \"You are an expert in the Crystal programming language\",\n\n  # You can pass the full list of messages, including messages it gave you\n  # back.\n  messages: [\n    Anthropic::Message.new(\"What makes Crystal the best programming language?\")\n  ],\n\n  # The maximum number of tokens the AI will try to respond with. Keep this low\n  # if you're feeding untrusted prompts.\n  max_tokens: 4096,\n\n  # A floating-point value between 0.0 and 1.0 representing how creative the\n  # response should be. Lower values (closer to 0.0) will be more deterministic\n  # and should be used for analytical prompts. Higher values (closer to 1.0)\n  # will be more stochastic.\n  temperature: 0.5,\n\n  # You can optionally pass an `Array` of tools to give the client a way to run\n  # custom code in your app. See below for additional information on how to\n  # define those. The more tools you pass in with a request, the more tokens the\n  # request will use, so you should keep this to a reasonable size.\n  #\n  # By default, no tools are included.\n  tools: [\n    GitHubUserLookup,\n    GoogleDriveSearch.new(google_oauth_token),\n  ],\n\n  # Uncomment the following line to avoid automatically running the tool\n  # selected by the model.\n  # run_tools: false,\n\n  # Limit the token selection to the \"top k\" tokens. If you need this\n  # explanation, chances are you should use `temperature` instead. That's not a\n  # dig — I've never used it myself.\n  # top_k: 10,\n\n  # P value for nucleus sampling. If you're dialing in your prompts with the\n  # `temperature` argument, you should ignore this one. I've never used this\n  # one, either.\n  # top_p: 0.1234,\n)\n```\n\nYou can also pass images to the model:\n\n```crystal\nputs claude\n  .messages\n  .create(\n    # You should generally use the Haiku model when dealing with images since\n    # they tend to consume quite a few tokens.\n    model: Anthropic.model_name(:haiku),\n    messages: [\n      Anthropic::Message.new(\n        content: Array(Anthropic::MessageContent){\n          # Images are base64-encoded and sent to the model\n          Anthropic::Image.base64(:jpeg, File.read(\"/path/to/image.jpeg\")),\n          Anthropic::Text.new(\"Describe this image\"),\n        },\n      ),\n    ],\n    max_tokens: 4096,\n    # Using a more deterministic response about the image\n    temperature: 0.1,\n  )\n```\n\n### Defining tools\n\nTools are objects that the Anthropic models can use to invoke your code. You\ncan define them easily with a `struct` that inherits from\n`Anthropic::Tool::Handler`.\n\n```crystal\nstruct GitHubUserLookup \u003c Anthropic::Tool::Handler\n  # Define any properties required for this tool as getters. Claude will\n  # provide them if it can based on the user's question.\n\n  # The username/login for the GitHub user, used to fetch the user from the\n  # GitHub API.\n  getter username : String\n\n  # This is the description that lets the model know when and how to use your\n  # code. It's basically the documentation the model will use. The more\n  # descriptive this is, the more confidence the model will have in invoking\n  # it, but it does consume tokens.\n  def self.description\n    \u003c\u003c-EOF\n      Retrieves the GitHub user with the given username. The username may also\n      be referred to as a \"login\". The username can only contain letters,\n      numbers, underscores, and dashes.\n\n      The tool will return the current data about the GitHub user with that\n      username. It should be used when the user asks about that particular\n      GitHub user. It will not provide information about GitHub repositories\n      or any issues, pull requests, commits, or other content on GitHub created\n      by that GitHub user.\n      EOF\n  end\n\n  # This is the method the client will use to invoke this tool. The return value\n  # of this method will be serialized as JSON and sent over the wire as the\n  # tool-use result\n  def call\n    User.from_json HTTP::Client.get(URI.parse(\"https://api.github.com/users/#{username}\")).body\n  end\n\n  # The definition for the value we want to send back to the model. Every\n  # property specified here will consume tokens, so only define getters that\n  # will provide useful context to the model.\n  struct User\n    include JSON::Serializable\n\n    getter login : String\n    getter name : String\n    getter company : String?\n    getter location : String\n    getter bio : String?\n    getter public_repos : Int64\n    getter public_gists : Int64\n    getter followers : Int64\n    getter following : Int64\n  end\nend\n```\n\nYou can also define tools without inheriting from `Anthropic::Tool::Handler`. That type simply implements the following API on the tool object (instance or type) being passed in:\n\n- `name : String`\n- `description : String`\n- `json_schema`, which returns a `to_json`-able object\n- `parse`, which returns a `call`-able object which returns a `to_json`-able object\n\nHere is an example of a tool that searches a user's Google Drive (via the [`jgaskins/google`](https://github.com/jgaskins/google) shard) using the provided query. Claude will generate the query and pass it to the tool, \n\n```crystal\nrequire \"google\"\nrequire \"google/drive\"\n\nrecord GoogleDriveSearch, token : String do\n  GOOGLE = Google::Client.new(\n    client_id: ENV[\"GOOGLE_CLIENT_ID\"],\n    client_secret: ENV[\"GOOGLE_CLIENT_SECRET\"],\n    redirect_uri: URI.parse(\"https://example.com/oauth2/google\"),\n  )\n\n  def description\n    \"Search Google for a user's documents with the given search query. You must use the Google Drive API query string format.\"\n  end\n\n  def name\n    \"GoogleDriveSearch\"\n  end\n\n  def json_schema\n    # The `json_schema` class method is provided on all `JSON::Serializable`\n    # types by the `spider-gazelle/json-schema` shard.\n    Query.json_schema\n  end\n\n  def parse(json : String)\n    query = Query.from_json json\n    query.search = self\n    query\n  end\n\n  def call(query : String)\n    files = GOOGLE\n      .drive\n      .files\n      .list(\n        token: token,\n        q: \"(#{query}) and mimeType contains 'application/vnd.google-apps'\",\n        limit: 10,\n      )\n      .to_a\n\n    array = Array(FileInfo).new(files.size)\n    # Requires this PR to be released, or the equivalent monkeypatch:\n    #   https://github.com/crystal-lang/crystal/pull/14837\n    WaitGroup.wait do |wg|\n      mutex = Mutex.new\n      files.each do |file|\n        wg.spawn do\n          file_info = FileInfo.new(\n            id: file.id,\n            name: file.name,\n            content: GOOGLE.drive.files.export(file, \"text/plain\", token, \u0026.gets_to_end),\n            link: file.web_view_link,\n          )\n\n          mutex.synchronize do\n            array \u003c\u003c file_info\n          end\n        end\n      end\n    end\n\n    array\n  end\n\n  struct FileInfo\n    include JSON::Serializable\n\n    getter id : String\n    getter name : String\n    getter content : String\n    getter link : String\n\n    def initialize(@id, @name, @content, @link)\n    end\n  end\n\n  struct Query\n    include JSON::Serializable\n\n    getter query : String\n    @[JSON::Field(ignore: true)]\n    protected property! search : GoogleDriveSearch\n\n    def call\n      search.call(query)\n    end\n  end\nend\n```\n\nSee the example code above to find out how to pass them to the model.\n\n## Contributing\n\n1. Fork it (\u003chttps://github.com/jgaskins/anthropic/fork\u003e)\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n\n## Contributors\n\n- [Jamie Gaskins](https://github.com/jgaskins) - creator and maintainer\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjgaskins%2Fanthropic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjgaskins%2Fanthropic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjgaskins%2Fanthropic/lists"}