Ollamaを使ってLlamaIndexでRAGを構築する【Windows 11 + WSL 2 + JupyterLab】
はじめに
LlamaIndexとOllamaは、自然言語処理(NLP)の分野で注目を集めている2つのツールです。
LlamaIndexは、大量のテキストデータを効率的に管理し、検索やクエリに応答するためのライブラリです。PDFや文書ファイルから情報を抽出し、インデックスを作成することで、ユーザーが求める情報をすばやく見つけ出すことができます。
一方、Ollamaは、オープンソースの言語モデルであるLLaMA(ラマ)をベースに構築された、対話型のAIアシスタントです。ユーザーとの自然な会話を通じて、質問に答えたり、タスクを支援したりすることができます。
本記事では、Windows 11環境にWSL 2(Ubuntu)とJupyterLabを設定し、LlamaIndexとOllamaを組み合わせてPDFファイルから情報を抽出し、クエリに応答する方法を初心者向けに解説します。コードの例示を交えながら、ステップバイステップで説明していきますので、プログラミングの経験が浅い方でも理解しやすい内容となっています。
本記事を通じて、LlamaIndexとOllamaを活用した文書管理と検索の基本的な流れをつかみ、自然言語処理の実践的なスキルを身につけることができるでしょう。それでは、早速始めていきましょう。
用語の説明
WSL (Windows Subsystem for Linux):
WSLは、Windows上でLinux環境を実行するための機能です。これにより、LinuxのコマンドやソフトウェアをWindowsで利用できます。
JupyterLab:
JupyterLabは、データサイエンスや機械学習のためのインタラクティブな開発環境です。コードの実行、データの可視化、ノートの作成が可能です。
環境構築
LlamaIndexとOllamaを使用するには、まずWindows 11上に適切な環境を設定する必要があります。ここでは、WSL 2(Ubuntu)とminicondaを使って、仮想環境を構築する方法を説明します。
環境
- Windows 11
- WSL2:Ubuntu 22.04 LTS
LlamaIndexのバージョン(本記事で必要ないパッケージも含まれているかもしれません)
llama-cpp-python 0.2.61
llama-index 0.10.28
llama-index-cli 0.1.11
llama-index-core 0.10.28
llama-index-embeddings-huggingface 0.2.0
llama-index-embeddings-langchain 0.1.2
llama-index-indices-managed-llama-cloud 0.1.5
llama-index-legacy 0.9.48
llama-index-llms-llama-cpp 0.1.3
llama-index-llms-ollama 0.1.2
llama-index-readers-file 0.1.16
llama-index-readers-llama-parse 0.1.4
llama-parse 0.4.0
llamaindex-py-client 0.1.16
WSL 2 (Ubuntu)環境とminicondaのセットアップ
まず、Microsoftの公式ドキュメントに従って、WSL 2とUbuntuをインストールしてください。インストールが完了したら、Ubuntuを起動し、次のコマンドを実行して、システムを最新の状態に更新します。
sudo apt update && sudo apt upgrade -y
次に、minicondaをインストールします。公式サイトからインストーラーをダウンロードし、次のコマンドを実行します。
bash Miniconda3-latest-Linux-x86_64.sh
インストールが完了したら、新しい仮想環境を作成します。
conda create -n myenv python=3.9
仮想環境をアクティベートするには、次のコマンドを使用します。
conda activate myenv
JupyterLabのインストールと起動
最後に、JupyterLabをインストールします。
conda install -c conda-forge jupyterlab
インストールが完了したら、次のコマンドでJupyterLabを起動できます。
jupyter lab
ブラウザが自動的に開き、JupyterLabのインターフェースが表示されます。これで、LlamaIndexとOllamaを使用するための環境が整いました。
上記の手順に従うことで、初心者でもWindows 11上にWSL 2(Ubuntu)とminicondaを使った仮想環境を構築し、JupyterLabを起動することができます。
LlamaIndexとOllamaのインストール
前のセクションで構築した仮想環境内で、LlamaIndexとOllamaをインストールします。
Ollamaのインストール
ここでは、Windowsデスクトップ版ではなく、DockerでOllamaをインストールする。
llama-indexとllama-index-llms-ollamaのインストール手順
まず、次のコマンドを実行して、llama-indexとllama-index-llms-ollamaをインストールします。
※JupyterLab以外(コマンドプロンプトなど)で実行する場合は、先頭の!
を外してください。
!pip install llama-index
!pip install llama-index-llms-ollama
!pip install llama-index-embeddings-huggingface
Ollamaクライアントの初期化方法
インストールが完了したら、JupyterLabでノートブックを新規作成し、以下のコードを実行してOllamaクライアントを初期化します。
from llama_index.llms.ollama import Ollama
# 使用モデルを選択します。
llm = Ollama(model="llama2", request_timeout=30.0)
このコードでは、Ollamaクラスをインポートし、model引数で使用するモデル名(ここではllama2
)を指定しています。request_timeout引数はリクエストのタイムアウト時間(秒)を設定します。
これで、LlamaIndexとOllamaが使用可能な状態になりました。
Ollamaクライアントを初期化する際、model引数で指定するモデル名は、Ollamaで提供されているモデルの中から選択します。また、request_timeout引数は、APIリクエストがタイムアウトするまでの時間を秒単位で指定します。デフォルト値は30秒ですが、必要に応じて長めに設定することができます。
LlamaIndexでのOllamaの基本的な使い方
LlamaIndexでOllamaを使用する際、主にcompleteメソッドとchatメソッドを使います。
completeメソッドの使用例
completeメソッドは、ユーザーの入力に対して、Ollamaが生成した回答を返します。以下は、completeメソッドの使用例です。
response = llm.complete("What is the capital of France?")
print(response)
このコードでは、"What is the capital of France?"という質問をcompleteメソッドに渡しています。Ollamaは、この質問に対する回答を生成し、response変数に格納します。最後に、print関数で回答を表示します。
chatメソッドの使用例
一方、chatメソッドは、ユーザーとOllamaの間で対話的なやり取りを行うために使用します。以下は、chatメソッドの使用例です。
from llama_index.core.llms import ChatMessage
messages = [
ChatMessage(role="system", content="You are a helpful assistant."),
ChatMessage(role="user", content="What is the weather like today?"),
]
response = llm.chat(messages)
print(response)
このコードでは、ChatMessageクラスを使って、システムメッセージとユーザーメッセージを定義しています。システムメッセージは、Ollamaの役割を指定し、ユーザーメッセージは、ユーザーからの質問を表します。これらのメッセージをchatメソッドに渡すことで、Ollamaとの対話が開始されます。
さらに、LlamaIndexではストリーミングAPIも提供しています。ストリーミングAPIを使うと、Ollamaの回答をリアルタイムで取得できます。stream_completeメソッドとstream_chatメソッドがそれぞれcompleteメソッドとchatメソッドに対応しています。
以下は、stream_completeメソッドの使用例です。
response = llm.stream_complete("What is the capital of France?")
for chunk in response:
print(chunk.delta, end="", flush=True)
このコードでは、stream_completeメソッドを使って、Ollamaの回答をストリーミングで取得しています。回答は、チャンクごとにchunk.deltaに格納され、print関数で順次表示されます。
同様に、stream_chatメソッドを使ってストリーミングで対話的なやり取りを行うこともできます。
以上が、LlamaIndexでのOllamaの基本的な使い方です。completeメソッドとchatメソッドを使って、質問に回答したり、対話を行ったりできます。
PDFからの情報抽出とクエリ応答
LlamaIndexとOllamaを使って、PDFファイルから情報を抽出し、ユーザーのクエリに応答する方法を見ていきましょう。
PDFの準備
お好きなPDFを用意します。今回は、厚生労働省が公開しているモデル就業規則を使わせていただきました。
# フォルダを作成します
!mkdir -p 'data/10k/'
# PDFをダウンロードします
!wget 'https://www.mhlw.go.jp/content/001018385.pdf' -O 'data/10k/001018385.pdf'
※10k
フォルダは特に意味はありません。省略可。
ダウンロード中でタイムアウトする場合は手動でダウンロードしてください。
HuggingFace Embeddingモデルの初期化
まず、HuggingFaceのEmbeddingモデルを初期化します。Embeddingモデルはsentence-transformers/all-MiniLM-L6-v2
という比較的小さくて高速なモデルを使います。
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
embed_model = HuggingFaceEmbedding(model_name="all-MiniLM-L6-v2")
PDFファイルの読み込みとインデックス作成
次に、PDFファイルを読み込み、インデックスを作成します。
from llama_index.core import SimpleDirectoryReader, VectorStoreIndex
reader = SimpleDirectoryReader(input_files=["./data/001018385.pdf"])
data = reader.load_data()
index = VectorStoreIndex.from_documents(data, embed_model=embed_model)
このコードでは、SimpleDirectoryReaderを使ってPDFファイルを読み込み、VectorStoreIndexを使ってインデックスを作成しています。
Ollamaを使ったクエリエンジンの初期化
続いて、Ollamaを使ったクエリエンジンを初期化します。
query_engine = index.as_query_engine(llm=llm, streaming=True, similarity_top_k=3)
ここでは、as_query_engineメソッドを使って、インデックスからクエリエンジンを初期化しています。llm引数でOllamaを指定し、streaming引数でストリーミングを有効にしています。また、similarity_top_k引数で、類似度の高い上位3つの結果を返すように設定しています。
ストリーミングでのレスポンス取得
クエリを実行し、ストリーミングでレスポンスを取得するには、以下のようにします。
response = query_engine.query("就業規則では休みは何日もらえますか?日本語で答えて。")
response.print_response_stream()
queryメソッドにクエリを渡し、print_response_streamメソッドでレスポンスをストリーミングで表示します。
ソースノードの確認と解析
最後に、ソースノードを確認し、解析します。
for node in response.source_nodes:
print(f"Text: {node.node.text[:100]}...")
print(f"Similarity score: {node.similarity_score}")
このコードでは、response.source_nodesからソースノードを取得し、各ノードのテキストと類似度スコアを表示しています。
出力結果
回答は以下になります。
Based on the provided context information, the answer to the query is:
就業規則では休みは3日もらえます。(65)
Just like how the law specifies that an employee is entitled to a maximum of 3 days of rest in a row, it also states that an employee must be given at least 24 hours of continuous rest time after each day of work. However, this rule does not apply in cases of natural disasters or other unavoidable circumstances. (36)-----
Text: なお、1か月60時間の算定には、法定休日に労働した時間数は含まれませんが、法 定外の休日に行った労働における時間外労働の時間数は含まれます。 ...
Metadata: {'page_label': '55', 'file_name': '001018385.pdf', 'file_path': 'data/10k/001018385.pdf', 'file_type': 'application/pdf', 'file_size': 1058782, 'creation_date': '2024-04-12', 'last_modified_date': '2023-07-07'}
Score: 0.321
-----
Text: - 65 - ません。しかし、賞与を支給する場合、就業規則に支給対象時期、賞与の算定基準、査 定期間、支払方法等を明確にしておくことが必要です。 2 就業規則に、賞与の支給対象者を一定の日(例えば、6月1日や12月1日、又は賞 与支給日)に在籍した者とする規定を設けることで、期間の途中で退職等し、その日に 在職しない者には支給しないこととすることも可能です。 ...
Metadata: {'page_label': '65', 'file_name': '001018385.pdf', 'file_path': 'data/10k/001018385.pdf', 'file_type': 'application/pdf', 'file_size': 1058782, 'creation_date': '2024-04-12', 'last_modified_date': '2023-07-07'}
Score: 0.306
-----
Text: [例1] インターバル時間と翌日の所定労働時間が重複する部分を働いたものとみなす 場合 (勤務間インターバル) 第22条 いかなる場合も、従業員ごとに1日の勤務終了後、次の勤務の開始までに少 なくとも、○時間の継続した休息時間を与える。ただし、災害その他避けることがで きない場合は、この限りではない。 ...
Metadata: {'page_label': '36', 'file_name': '001018385.pdf', 'file_path': 'data/10k/001018385.pdf', 'file_type': 'application/pdf', 'file_size': 1058782, 'creation_date': '2024-04-12', 'last_modified_date': '2023-07-07'}
Score: 0.306
あえ~。3日ですか…。
テキストファイルからの情報抽出とクエリ応答
PDFだけではなく、テキストファイルからも情報を抽出して回答してくれます。今回は、npaka先生のやり方を使わせていただきました。
変更した箇所は、以下になります。bocchi.txtというファイルを新たに用意しました。文化祭ライブ
という単語だけで回答できるのでしょうか?
# PDF(テキスト)の読み込み
reader = SimpleDirectoryReader(input_files=["./data/10k/bocchi.txt"])
data = reader.load_data()
# ストリーミングでレスポンスを取得
response = query_engine.query(
"文化祭ライブはどうだったのか**日本語**で簡潔に説明してください。 "
" page reference after each statement."
)
response.print_response_stream()
質問に対する回答は以下になります。
文化祭ライブは、後藤ひとりが大きな不安を抱えつつも、周囲の励ましと協力によって成功を収めた。ひとりは、喜多郁代の後押しもあり、ライブをすることを決めたが、ライブ中に機材トラブルが発生し、ひとりはとっさにボトルネック奏法のアドリブを行い、仲間たちはそのフォローに回る。演奏をみんなから拍手喝采されるひとりだったが、コミュ症のひとりはその晴れ舞台に混乱し、観客席にダイブするという暴挙に出てしまう。結果としては成功を収めたものの、ひとりの奇行によって、結束バンドの名はフリーライターの佐藤愛子の耳に入ることとなる。---
In summary, the cultural festival live was successful for later Hiro, but his comedic behavior caused him to accidentally dive into the audience seats. Despite this, the performance was well-received by the audience and marked a successful debut for the band.-----
夏休みに入り、後藤ひとりは知り合いも増えていたが、自分から遊びに誘うことができずに夏休みも終わり、新学期を迎えていた。相も変わらずコミュ症なひとりは文化祭で盛り上がるクラスメイトをよそに、一人黄昏ていた。 ...ギターの腕前はかなりのものとなっていたが、気がつけば中学は卒業。結局、友達を作るという当初の目標は達成することはできずじまいだった。そして高校に入学したひとりはギター演奏を動画配信し、「ギターヒーロー」としてネットでそこそこの人気を集めていたが、現実では変わらず友達を作れずにいた。ひとりはギターをこれみよがしに持ち、ギタリストだということをアピールするものの効果はなし。黄昏(たそがれ)ていたところ、ギタリストを探していた伊地知虹夏に誘われ、バンド活動をすることとなる。ひとりは虹夏、山田リョウらと共に「結束バンド」として活動を始めるが、友達は欲しいけどコミュ症独りぼっちのひとりは問題行動ばかり起こしていた。結束バンドなのに、結束力ゼロな凸凹バンド活動は幕開けするのだった。そしてそんなひとりに、クラスメイトの喜多郁代が話しかけてきて、彼女が結束バンドの元メンバーだったことを知る。紆余(うよ)曲折の末、郁代はひとりとの交流で結束バンドに戻ることになり、ひとりたちはライブに向けて準備をすることとなる。ひとりはライブチケットのノルマをこなすため、方々を回ってチケットを売ることになるが、人見知りのひとりには難題ですぐに壁にぶち当たってしまう。絶望するひとりは、そこで行き倒れた廣井きくりと遭遇。彼女の計らいで突発的な路上ライブを行なって、観客を魅了する。きくりのお陰でライブチケットのノルマもこなし、結束バンドはライブに臨むのだった。
Metadata: {'file_path': 'data/10k/bocchi.txt', 'file_name': 'bocchi.txt', 'file_type': 'text/plain', 'file_size': 31713, 'creation_date': '2024-04-11', 'last_modified_date': '2024-04-04'}
Score: 0.095
-----
人気メタルバンド「SIDEROS(シデロス)」のリーダーを務める少女。ギター兼ボーカルを担当している。ライブハウス「FOLT」で活動しており、廣井きくりを純粋に慕っている数少ない人物の一人。小心者なのに意地っ張りという難儀な性格をしており、弱い自分を見せまいとつい強がりをしてしまう。コミュニケーションを取るのも下手で敵をつくりがちであるため、友達もおらず、バンドメンバーも入れ替わりが激しい。きくりはそんな自分にも構ってくれるために慕っているが、飯をたかられたり、お金を貸したり、便利に使われていることに気づいていない。 ...ては憤りを感じており、彼女が一時期FOLTから姿を消した際には平和を満喫していた。
Metadata: {'file_path': 'data/10k/bocchi.txt', 'file_name': 'bocchi.txt', 'file_type': 'text/plain', 'file_size': 31713, 'creation_date': '2024-04-11', 'last_modified_date': '2024-04-04'}
Score: 0.076
-----
未確認ライオットに参加するためには、デモ審査、ウェブ投票、ライブ審査を通過して、最終審査に合格する必要があった。そして結束バンドは、デモ審査のためのMV(ミュージックビデオ)作成を始める。そんな中、結束バンドの面々は、フェスではライバルとなる人気メタルバンド「SIDEROS」に所属する大槻ヨヨコと出会う。ひねくれ者なヨヨコは、結束バンドをライバル視しながらも、彼女たちにアドバイスを送り、ファンたちの助けもあってMVを完成させる。デモ審査の結果が出るのを待つばかりとなり、新たな春を迎える。ひとりたちは無事進級するものの、相も変わらずコミュ症のひとりは奇行が目立っていた。 ...拍手喝采されるひとりだったが、コミュ症のひとりはその晴れ舞台に混乱し、観客席にダイブするという暴挙に出てしまう。ひとまずライブは成功を収めるものの、ひとりの奇行により、結束バンドの名はフリーライターの佐藤愛子の耳に入ることとなる。そして愛子が結束バンドを取材した際に、ひとりが「ギターヒーロー」であることがバレてしまう。愛子はひとりに強い関心を抱くが、結束バンドの演奏に対しては辛らつで、彼女たちを酷評する。そしてひとりたちは、愛子を見返すため、10代限定のロックフェス「未確認ライオット」に参加し、グランプリを目指すことを決める。
Metadata: {'file_path': 'data/10k/bocchi.txt', 'file_name': 'bocchi.txt', 'file_type': 'text/plain', 'file_size': 31713, 'creation_date': '2024-04-11', 'last_modified_date': '2024-04-04'}
Score: 0.073
回答するためにテキストのどこを参照したのかも出力されているようです。
まとめ
LlamaIndexとOllamaを組み合わせることで、PDFファイルから情報を効率的に抽出し、ユーザーのクエリに対して適切な回答を提供できます。この手法は、大量の文書を扱う必要がある場合に特に有効です。
LlamaIndexとOllamaは、PDFファイル以外にも、様々なテキストデータに応用できます。例えば、ウェブページ、ニュース記事、レポートなどから情報を抽出し、ユーザーの質問に答えるシステムを構築することができます。また、会話型のインターフェースを提供することで、ユーザーとのインタラクションを向上させることも可能です。
今後、LlamaIndexとOllamaのようなツールは、自然言語処理の分野でますます重要な役割を果たすことが期待されます。大規模な言語モデルと効率的なインデックス作成技術を組み合わせることで、より高度な情報検索システムの開発が可能になるでしょう。また、ユーザーとのインタラクションをより自然でスムーズなものにするために、対話システムの改善にも注目が集まるかもしれません。
よくあるエラーと対処法
エラー: "Command not found"
対策: WSLが正しくインストールされているか確認し、必要なソフトウェアがインストールされているかチェックしてください。
エラー: "Permission denied"
対策: 管理者権限で実行するか、ファイルのアクセス権を変更してください。
参考資料
Discussion
ストリーミングでのレスポンス取得を実施するとConnectErrorになってしまいます。
知識不足で原因がわからず…。もしわかりましたら教えていただけますか。
コメントありがとうございます!
このエラー内容からですとおそらくですが、ネットワーク接続関係の問題の可能性があります。
(もしくはファイアウォールが特定の接続をブロックしているのかもしれません)
もう少し状況を教えていただけますと解決が見つかるかもしれません。
返信ありがとうございます。以下がエラーの全文です。
ConnectError Traceback (most recent call last)
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/httpx/_transports/default.py:69, in map_httpcore_exceptions()
68 try:
---> 69 yield
70 except Exception as exc:
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/httpx/_transports/default.py:233, in HTTPTransport.handle_request(self, request)
232 with map_httpcore_exceptions():
--> 233 resp = self._pool.handle_request(req)
235 assert isinstance(resp.stream, typing.Iterable)
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/httpcore/_sync/connection_pool.py:216, in ConnectionPool.handle_request(self, request)
215 self._close_connections(closing)
--> 216 raise exc from None
218 # Return the response. Note that in this case we still have to manage
219 # the point at which the response is closed.
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/httpcore/_sync/connection_pool.py:196, in ConnectionPool.handle_request(self, request)
194 try:
195 # Send the request on the assigned connection.
--> 196 response = connection.handle_request(
197 pool_request.request
198 )
199 except ConnectionNotAvailable:
200 # In some cases a connection may initially be available to
201 # handle a request, but then become unavailable.
202 #
203 # In this case we clear the connection and try again.
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/httpcore/_sync/connection.py:99, in HTTPConnection.handle_request(self, request)
98 self._connect_failed = True
---> 99 raise exc
101 return self._connection.handle_request(request)
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/httpcore/_sync/connection.py:76, in HTTPConnection.handle_request(self, request)
75 if self._connection is None:
---> 76 stream = self._connect(request)
78 ssl_object = stream.get_extra_info("ssl_object")
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/httpcore/_sync/connection.py:122, in HTTPConnection._connect(self, request)
121 with Trace("connect_tcp", logger, request, kwargs) as trace:
--> 122 stream = self._network_backend.connect_tcp(**kwargs)
123 trace.return_value = stream
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/httpcore/_backends/sync.py:213, in SyncBackend.connect_tcp(self, host, port, timeout, local_address, socket_options)
212 sock.setsockopt(*option) # pragma: no cover
--> 213 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
214 return SyncStream(sock)
File ~/miniconda3/envs/myenv/lib/python3.9/contextlib.py:137, in _GeneratorContextManager.exit(self, typ, value, traceback)
136 try:
--> 137 self.gen.throw(typ, value, traceback)
138 except StopIteration as exc:
139 # Suppress StopIteration unless it's the same exception that
140 # was passed to throw(). This prevents a StopIteration
141 # raised inside the "with" statement from being suppressed.
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/httpcore/_exceptions.py:14, in map_exceptions(map)
13 if isinstance(exc, from_exc):
---> 14 raise to_exc(exc) from exc
15 raise
ConnectError: [Errno 111] Connection refused
The above exception was the direct cause of the following exception:
ConnectError Traceback (most recent call last)
Cell In[6], line 2
1 response = query_engine.query("就業規則では休みは何日もらえますか?日本語で答えて。")
----> 2 response.print_response_stream()
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/llama_index/core/base/response/schema.py:142, in StreamingResponse.print_response_stream(self)
140 if self.response_txt is None and self.response_gen is not None:
141 response_txt = ""
--> 142 for text in self.response_gen:
143 print(text, end="", flush=True)
144 response_txt += text
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/llama_index/core/llms/llm.py:111, in stream_chat_response_to_tokens.<locals>.gen()
110 def gen() -> TokenGen:
--> 111 for response in chat_response_gen:
112 yield response.delta or ""
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/llama_index/core/llms/callbacks.py:185, in llm_chat_callback.<locals>.wrap.<locals>.wrapped_llm_chat.<locals>.wrapped_gen()
183 last_response = None
184 try:
--> 185 for x in f_return_val:
186 dispatcher.event(
187 LLMChatInProgressEvent(
188 messages=messages,
(...)
191 )
192 )
193 yield cast(ChatResponse, x)
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/llama_index/llms/ollama/base.py:173, in Ollama.stream_chat(self, messages, **kwargs)
170 payload["format"] = "json"
172 with httpx.Client(timeout=Timeout(self.request_timeout)) as client:
--> 173 with client.stream(
174 method="POST",
175 url=f"{self.base_url}/api/chat",
176 json=payload,
177 ) as response:
178 response.raise_for_status()
179 text = ""
File ~/miniconda3/envs/myenv/lib/python3.9/contextlib.py:119, in _GeneratorContextManager.enter(self)
117 del self.args, self.kwds, self.func
118 try:
--> 119 return next(self.gen)
120 except StopIteration:
121 raise RuntimeError("generator didn't yield") from None
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/httpx/_client.py:870, in Client.stream(self, method, url, content, data, files, json, params, headers, cookies, auth, follow_redirects, timeout, extensions)
847 """
848 Alternative to
httpx.request()
that streams the response body849 instead of loading it into memory at once.
(...)
855 [0]: /quickstart#streaming-responses
856 """
857 request = self.build_request(
858 method=method,
859 url=url,
(...)
868 extensions=extensions,
869 )
--> 870 response = self.send(
871 request=request,
872 auth=auth,
873 follow_redirects=follow_redirects,
874 stream=True,
875 )
876 try:
877 yield response
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/httpx/_client.py:914, in Client.send(self, request, stream, auth, follow_redirects)
906 follow_redirects = (
907 self.follow_redirects
908 if isinstance(follow_redirects, UseClientDefault)
909 else follow_redirects
910 )
912 auth = self._build_request_auth(request, auth)
--> 914 response = self._send_handling_auth(
915 request,
916 auth=auth,
917 follow_redirects=follow_redirects,
918 history=[],
919 )
920 try:
921 if not stream:
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/httpx/_client.py:942, in Client._send_handling_auth(self, request, auth, follow_redirects, history)
939 request = next(auth_flow)
941 while True:
--> 942 response = self._send_handling_redirects(
943 request,
944 follow_redirects=follow_redirects,
945 history=history,
946 )
947 try:
948 try:
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/httpx/_client.py:979, in Client._send_handling_redirects(self, request, follow_redirects, history)
976 for hook in self._event_hooks["request"]:
977 hook(request)
--> 979 response = self._send_single_request(request)
980 try:
981 for hook in self._event_hooks["response"]:
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/httpx/_client.py:1015, in Client._send_single_request(self, request)
1010 raise RuntimeError(
1011 "Attempted to send an async request with a sync Client instance."
1012 )
1014 with request_context(request=request):
-> 1015 response = transport.handle_request(request)
1017 assert isinstance(response.stream, SyncByteStream)
1019 response.request = request
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/httpx/_transports/default.py:233, in HTTPTransport.handle_request(self, request)
220 req = httpcore.Request(
221 method=request.method,
222 url=httpcore.URL(
(...)
230 extensions=request.extensions,
231 )
232 with map_httpcore_exceptions():
--> 233 resp = self._pool.handle_request(req)
235 assert isinstance(resp.stream, typing.Iterable)
237 return Response(
238 status_code=resp.status,
239 headers=resp.headers,
240 stream=ResponseStream(resp.stream),
241 extensions=resp.extensions,
242 )
File ~/miniconda3/envs/myenv/lib/python3.9/contextlib.py:137, in _GeneratorContextManager.exit(self, typ, value, traceback)
135 value = typ()
136 try:
--> 137 self.gen.throw(typ, value, traceback)
138 except StopIteration as exc:
139 # Suppress StopIteration unless it's the same exception that
140 # was passed to throw(). This prevents a StopIteration
141 # raised inside the "with" statement from being suppressed.
142 return exc is not value
File ~/miniconda3/envs/myenv/lib/python3.9/site-packages/httpx/_transports/default.py:86, in map_httpcore_exceptions()
83 raise
85 message = str(exc)
---> 86 raise mapped_exc(message) from exc
ConnectError: [Errno 111] Connection refused
エラーの全文を共有いただきありがとうございます。
ConnectError: [Errno 111] Connection refused
とありますので、以下の可能性が考えられます。具体的な回答になっておりませんが、この辺りをお調べいただけますでしょうか。