config.yml 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. version: 2.1
  2. orbs:
  3. python: circleci/python@2
  4. commands:
  5. run_chatgpt_api_test:
  6. parameters:
  7. inference_engine:
  8. type: string
  9. model_id:
  10. type: string
  11. expected_output:
  12. type: string
  13. prompt:
  14. type: string
  15. steps:
  16. - run:
  17. name: Run chatgpt api integration test (<<parameters.inference_engine>>, <<parameters.model_id>>)
  18. command: |
  19. source env/bin/activate
  20. # Set CLANG=1 for tinygrad only
  21. if [ "<<parameters.inference_engine>>" = "tinygrad" ]; then
  22. pip install llvmlite
  23. export TOKENIZERS_PARALLELISM=true SUPPORT_BF16=0 CLANG=1
  24. fi
  25. # Start first instance
  26. HF_HOME="$(pwd)/.hf_cache_node1" DEBUG_DISCOVERY=7 DEBUG=7 exo --inference-engine <<parameters.inference_engine>> --node-id "node1" --listen-port 5678 --broadcast-port 5679 --chatgpt-api-port 8000 --chatgpt-api-response-timeout 900 --disable-tui 2>&1 | tee output1.log &
  27. PID1=$!
  28. # Start second instance
  29. HF_HOME="$(pwd)/.hf_cache_node2" DEBUG_DISCOVERY=7 DEBUG=7 exo --inference-engine <<parameters.inference_engine>> --node-id "node2" --listen-port 5679 --broadcast-port 5678 --chatgpt-api-port 8001 --chatgpt-api-response-timeout 900 --disable-tui 2>&1 | tee output2.log &
  30. PID2=$!
  31. # Wait for discovery
  32. sleep 10
  33. # Function to check if processes are still running
  34. check_processes() {
  35. if ! kill -0 $PID1 2>/dev/null; then
  36. echo "First instance (PID $PID1) died unexpectedly. Log output:"
  37. cat output1.log
  38. exit 1
  39. fi
  40. if ! kill -0 $PID2 2>/dev/null; then
  41. echo "Second instance (PID $PID2) died unexpectedly. Log output:"
  42. cat output2.log
  43. exit 1
  44. fi
  45. }
  46. # Check processes before proceeding
  47. check_processes
  48. echo "Sending request to first instance..."
  49. response_1=$(curl -s http://localhost:8000/v1/chat/completions \
  50. -H "Content-Type: application/json" \
  51. -d '{
  52. "model": "<<parameters.model_id>>",
  53. "messages": [{"role": "user", "content": "<<parameters.prompt>>"}],
  54. "temperature": 0.7
  55. }')
  56. echo "Response 1: $response_1"
  57. # Check processes after first response
  58. check_processes
  59. echo "Sending request to second instance..."
  60. response_2=$(curl -s http://localhost:8001/v1/chat/completions \
  61. -H "Content-Type: application/json" \
  62. -d '{
  63. "model": "<<parameters.model_id>>",
  64. "messages": [{"role": "user", "content": "<<parameters.prompt>>"}],
  65. "temperature": 0.7
  66. }')
  67. echo "Response 2: $response_2"
  68. # Check processes after second response
  69. check_processes
  70. # Stop both instances
  71. kill $PID1 $PID2
  72. echo ""
  73. # Extract content using jq and check if it contains expected output
  74. content1=$(echo "$response_1" | jq -r '.choices[0].message.content')
  75. content2=$(echo "$response_2" | jq -r '.choices[0].message.content')
  76. if [[ "$content1" != *"<<parameters.expected_output>>"* ]] || [[ "$content2" != *"<<parameters.expected_output>>"* ]]; then
  77. echo "Test failed: Response does not match '<<parameters.expected_output>>'"
  78. echo "Response 1 content: $content1"
  79. echo ""
  80. echo "Response 2 content: $content2"
  81. echo "Output of first instance:"
  82. cat output1.log
  83. echo "Output of second instance:"
  84. cat output2.log
  85. exit 1
  86. else
  87. echo "Test passed: Response from both nodes matches '<<parameters.expected_output>>'"
  88. fi
  89. jobs:
  90. unit_test:
  91. macos:
  92. xcode: "16.0.0"
  93. resource_class: m2pro.large
  94. steps:
  95. - checkout
  96. - run:
  97. name: Set up Python
  98. command: |
  99. brew install python@3.12
  100. python3.12 -m venv env
  101. source env/bin/activate
  102. - run:
  103. name: Install dependencies
  104. command: |
  105. source env/bin/activate
  106. pip install --upgrade pip
  107. pip install .
  108. - run:
  109. name: Run tests
  110. command: |
  111. source env/bin/activate
  112. # set TEMPERATURE to 0 for deterministic sampling
  113. echo "Running inference engine tests..."
  114. METAL_DEVICE_WRAPPER_TYPE=1 METAL_DEBUG_ERROR_MODE=0 METAL_XCODE=1 TEMPERATURE=0 python3 -m exo.inference.test_inference_engine
  115. echo "Running tokenizer tests..."
  116. python3 ./test/test_tokenizers.py
  117. python3 ./test/test_model_helpers.py
  118. discovery_integration_test:
  119. macos:
  120. xcode: "16.0.0"
  121. steps:
  122. - checkout
  123. - run:
  124. name: Set up Python
  125. command: |
  126. brew install python@3.12
  127. python3.12 -m venv env
  128. source env/bin/activate
  129. - run:
  130. name: Install dependencies
  131. command: |
  132. source env/bin/activate
  133. pip install --upgrade pip
  134. pip install .
  135. - run:
  136. name: Run discovery integration test
  137. command: |
  138. source env/bin/activate
  139. DEBUG_DISCOVERY=7 DEBUG=7 exo --node-id "node1" --listen-port 5678 --broadcast-port 5679 --chatgpt-api-port 8000 --disable-tui > output1.log 2>&1 &
  140. PID1=$!
  141. DEBUG_DISCOVERY=7 DEBUG=7 exo --node-id "node2" --listen-port 5679 --broadcast-port 5678 --chatgpt-api-port 8001 --disable-tui > output2.log 2>&1 &
  142. PID2=$!
  143. sleep 10
  144. kill $PID1 $PID2
  145. if grep -q "Peer statuses: {\\'node2\\': \\'is_connected=True, health_check=True" output1.log && ! grep -q "Failed to connect peers:" output1.log && grep -q "Peer statuses: {\\'node1\\': \\'is_connected=True, health_check=True" output2.log && ! grep -q "Failed to connect peers:" output2.log; then
  146. echo "Test passed: Both instances discovered each other"
  147. exit 0
  148. else
  149. echo "Test failed: Devices did not discover each other"
  150. echo "Output of first instance:"
  151. cat output1.log
  152. echo "Output of second instance:"
  153. cat output2.log
  154. exit 1
  155. fi
  156. chatgpt_api_integration_test_mlx:
  157. macos:
  158. xcode: "16.0.0"
  159. resource_class: m2pro.large
  160. steps:
  161. - checkout
  162. - run:
  163. name: Set up Python
  164. command: |
  165. brew install python@3.12
  166. python3.12 -m venv env
  167. source env/bin/activate
  168. - run:
  169. name: Install dependencies
  170. command: |
  171. source env/bin/activate
  172. pip install --upgrade pip
  173. pip install .
  174. - run_chatgpt_api_test:
  175. inference_engine: mlx
  176. model_id: llama-3.2-1b
  177. prompt: "Keep responses concise. Who was the king of pop?"
  178. expected_output: "Michael Jackson"
  179. chatgpt_api_integration_test_dummy:
  180. macos:
  181. xcode: "16.0.0"
  182. resource_class: m2pro.large
  183. steps:
  184. - checkout
  185. - run:
  186. name: Set up Python
  187. command: |
  188. brew install python@3.12
  189. python3.12 -m venv env
  190. source env/bin/activate
  191. - run:
  192. name: Install dependencies
  193. command: |
  194. source env/bin/activate
  195. pip install --upgrade pip
  196. pip install .
  197. - run_chatgpt_api_test:
  198. inference_engine: dummy
  199. model_id: dummy
  200. prompt: "Dummy prompt."
  201. expected_output: "dummy"
  202. chatgpt_api_integration_test_tinygrad:
  203. macos:
  204. xcode: "16.0.0"
  205. resource_class: m2pro.large
  206. steps:
  207. - checkout
  208. - run:
  209. name: Set up Python
  210. command: |
  211. brew install python@3.12
  212. python3.12 -m venv env
  213. source env/bin/activate
  214. - run:
  215. name: Install dependencies
  216. command: |
  217. source env/bin/activate
  218. pip install --upgrade pip
  219. pip install .
  220. - run_chatgpt_api_test:
  221. inference_engine: tinygrad
  222. model_id: llama-3.2-1b
  223. prompt: "Keep responses concise. Who was the king of pop?"
  224. expected_output: "Michael Jackson"
  225. measure_pip_sizes:
  226. macos:
  227. xcode: "16.0.0"
  228. steps:
  229. - checkout
  230. - run:
  231. name: Set up Python
  232. command: |
  233. brew install python@3.12
  234. python3.12 -m venv env
  235. source env/bin/activate
  236. - run:
  237. name: Install dependencies and measure sizes
  238. command: |
  239. source env/bin/activate
  240. pip install --upgrade pip
  241. pip install .
  242. python ./extra/pipsize.py --json ./pipsize.json
  243. - store_artifacts:
  244. path: ./pipsize.json
  245. destination: pip-sizes.json
  246. check_line_count:
  247. docker:
  248. - image: cimg/python:3.10
  249. steps:
  250. - checkout
  251. - run:
  252. name: Setup git for PR comparison
  253. command: |
  254. if [[ -n "$CIRCLE_PULL_REQUEST" ]]; then
  255. PR_NUMBER=$(echo $CIRCLE_PULL_REQUEST | rev | cut -d'/' -f1 | rev)
  256. BASE_BRANCH=$(curl -s -H "Circle-Token: $CIRCLE_TOKEN" \
  257. "https://circleci.com/api/v2/project/github/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/pipeline/$CIRCLE_WORKFLOW_ID" \
  258. | jq -r '.target_branch')
  259. git clone -b $BASE_BRANCH --single-branch \
  260. https://github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME.git \
  261. base_branch
  262. fi
  263. - run:
  264. name: Install dependencies
  265. command: |
  266. python -m pip install --upgrade pip
  267. pip install tabulate
  268. - run:
  269. name: Run line count check
  270. command: |
  271. if [[ -n "$CIRCLE_PULL_REQUEST" ]]; then
  272. python extra/line_counter.py base_branch .
  273. else
  274. python extra/line_counter.py .
  275. fi
  276. - store_artifacts:
  277. path: line-count-snapshot.json
  278. destination: line-count-snapshot.json
  279. - store_artifacts:
  280. path: line-count-diff.json
  281. destination: line-count-diff.json
  282. - run:
  283. name: Create test results directory
  284. command: |
  285. mkdir -p test-results/line-count
  286. cp line-count-*.json test-results/line-count/
  287. - store_test_results:
  288. path: test-results
  289. workflows:
  290. version: 2
  291. build_and_test:
  292. jobs:
  293. - check_line_count:
  294. filters:
  295. branches:
  296. only: /.*/
  297. tags:
  298. only: /.*/
  299. - unit_test
  300. - discovery_integration_test
  301. - chatgpt_api_integration_test_mlx
  302. - chatgpt_api_integration_test_tinygrad
  303. - chatgpt_api_integration_test_dummy
  304. - measure_pip_sizes