From efb30368a1fa8ae02ad51182d8de231c3de6c3b9 Mon Sep 17 00:00:00 2001 From: James Ketrenos Date: Thu, 20 Mar 2025 10:54:31 -0700 Subject: [PATCH] icon is working --- jupyter/stock.py | 25 ++++++++++++++----------- src/client/app/(tabs)/about.tsx | 5 +++-- src/client/app/(tabs)/index.tsx | 4 ++-- src/client/app/_layout.tsx | 16 ++++++++++++---- src/client/assets/favicon.ico | Bin 0 -> 318 bytes src/client/assets/images/favicon.ico | Bin 0 -> 318 bytes src/client/assets/images/favicon.png | Bin 1466 -> 4716 bytes src/client/package-lock.json | 23 +++++++++++++++++++++++ src/client/package.json | 1 + 9 files changed, 55 insertions(+), 19 deletions(-) create mode 100755 src/client/assets/favicon.ico create mode 100755 src/client/assets/images/favicon.ico diff --git a/jupyter/stock.py b/jupyter/stock.py index 610e88e..7e452e7 100644 --- a/jupyter/stock.py +++ b/jupyter/stock.py @@ -692,14 +692,8 @@ class WebServer: def setup_routes(self): """Setup Flask routes""" - @self.app.route('/') - def serve(): - return send_from_directory(self.app.static_folder, 'index.html') - def index(): - return render_template('index.html') - # Basic endpoint for chat completions - @self.app.route('/chat', methods=['POST']) + @self.app.route('/api/chat', methods=['POST']) async def chat(): if not irc_bot: return jsonify({ "error": "Bot not initialized" }), 400 @@ -715,29 +709,38 @@ class WebServer: }), 400 # Context requests - @self.app.route('/history', methods=['GET']) + @self.app.route('/api/history', methods=['GET']) def http_history(): if not irc_bot: return jsonify({ "error": "Bot not initialized" }), 400 return jsonify(irc_bot.history), 200 - @self.app.route('/system', methods=['GET']) + @self.app.route('/api/system', methods=['GET']) def http_system(): if not irc_bot: return jsonify({ "error": "Bot not initialized" }), 400 return jsonify(system_log), 200 - @self.app.route('/tools', methods=['GET']) + @self.app.route('/api/tools', methods=['GET']) def http_tools(): if not irc_bot: return jsonify({ "error": "Bot not initialized" }), 400 return jsonify(tool_log), 200 # Health check endpoint - @self.app.route('/health', methods=['GET']) + @self.app.route('/api/health', methods=['GET']) def health(): return jsonify({"status": "healthy"}), 200 + # Serve React app - This catches all routes not matched by API endpoints + @self.app.route('/', defaults={'path': ''}) + @self.app.route('/') + def serve(path): + if path != "" and os.path.exists(self.app.static_folder + '/' + path): + return send_from_directory(self.app.static_folder, path) + else: + return send_from_directory(self.app.static_folder, 'index.html') + def run(self, host='0.0.0.0', port=5000, debug=False, **kwargs): """Run the web server""" # Load documents diff --git a/src/client/app/(tabs)/about.tsx b/src/client/app/(tabs)/about.tsx index 7e1ee63..d446d6d 100644 --- a/src/client/app/(tabs)/about.tsx +++ b/src/client/app/(tabs)/about.tsx @@ -4,13 +4,14 @@ export default function AboutScreen() { return ( -
Welcome to my AI agent. It has live access to websites, weather, and stock information. You can ask it things like: +
Welcome to Ketr-AI. This AI agent has live access to websites, weather, and stock information. You can ask it things like:
    -
  • What's the current weather?
  • +
  • What's the current weather in Kansas?
  • Can you provide the current headlines from http://cnn.com?
  • What is the current value of the 5 most traded companies?
+
Internally, the system is using the LLAMA3.2 large language model, currently running locally in ollama. Various tools have been enabled for the LLM to use.
); diff --git a/src/client/app/(tabs)/index.tsx b/src/client/app/(tabs)/index.tsx index 0fc090f..23f7153 100644 --- a/src/client/app/(tabs)/index.tsx +++ b/src/client/app/(tabs)/index.tsx @@ -28,7 +28,7 @@ const App = () => { useEffect(() => { const fetchHistory = async () => { try { - const response = await fetch(`${url}/history`); + const response = await fetch(`${url}/api/history`); const data = await response.json(); if (conversation.length != data.length) setConversation(data || []); @@ -69,7 +69,7 @@ const App = () => { try { setLoading(true); // Send query to server - const response = await fetch(`${url}/chat`, { + const response = await fetch(`${url}/api/chat`, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/src/client/app/_layout.tsx b/src/client/app/_layout.tsx index 56c0d20..bfec16f 100644 --- a/src/client/app/_layout.tsx +++ b/src/client/app/_layout.tsx @@ -1,10 +1,18 @@ import { Stack } from 'expo-router'; +import { Helmet } from 'react-helmet'; export default function RootLayout() { return ( - - - - + <> + + Ketr-AI for Everyone + + + + + + + + ); } diff --git a/src/client/assets/favicon.ico b/src/client/assets/favicon.ico new file mode 100755 index 0000000000000000000000000000000000000000..ddf3b88c478d75c8263792b26d8c4ff96c4f4339 GIT binary patch literal 318 zcmZQzU<5(|0RbS%!l1#(z#zuJz@P!d0zj+)#2|5;fQf;DUr2yKN=Ax7T~mX>(#DD* zEHa!SGbf9ot+SnB#p;y|2agPW1OW1UZ7To( literal 0 HcmV?d00001 diff --git a/src/client/assets/images/favicon.ico b/src/client/assets/images/favicon.ico new file mode 100755 index 0000000000000000000000000000000000000000..ddf3b88c478d75c8263792b26d8c4ff96c4f4339 GIT binary patch literal 318 zcmZQzU<5(|0RbS%!l1#(z#zuJz@P!d0zj+)#2|5;fQf;DUr2yKN=Ax7T~mX>(#DD* zEHa!SGbf9ot+SnB#p;y|2agPW1OW1UZ7To( literal 0 HcmV?d00001 diff --git a/src/client/assets/images/favicon.png b/src/client/assets/images/favicon.png index e75f697b1801871ad8cd9309b05e8ffe8c6b6d01..9ad6bf37c46bfcd4dbdfc2f22097a703cf99884c 100644 GIT binary patch literal 4716 zcmeHKdsGuw8V_iNA}@;-#k$M}e5^9bH|cn zS}9n>7g(sciYV)@V6C`SK?M;Xi(0$&fdws!(C&h@_9h_0*`BksDCZky}=>DPMxB9AJ1?0#d4^80^YlaQJhkG@=%wd9=Nu9*85Y%ckf z*>wB4EgJ6_X1jh)h)+TxtE2>lUUiV!cW);yc1gOt?>!2|#+wKYl}kcHUq=IC*j2jq zbi`TtO0qax!p$CKVR5=j}B@ng#jSYBqJ0-t?Q)@( z_y?MGsA4%zLMeDEp%GG_)}5w81SX^|@|7Y|T_~<1!n5>vY?drek+odG$Ebk;4*q5V zAW-8Z3YpdE8iT+rq*{3e;Mp=wr$SZ;xm-w1kjkM@tsaNiG&T)^#b(0Dqy{)Z{(4L) zh!KSiQGk({nJvQP|ih;V~K zPv}TOtAQ+>Xo@z26jG^R9eVAbS|^nb(`yVvDgZs`W>iOK&=9&>O&@7tAjL*NGUU*2 zS{UNMA)?3N25p93fs2i}hV&XqfhmUVbs73}YdDyKj;G^l05yPJ8KbtGFOkZJEi4kG z5^9~*3Wz-lNfOF8VvX|6vSJNqq$9w5n0FNVb?jC!KuM(nkyepmaZe%=QZ4%nFs*{X z1lC6`hs{wiQ4B_X6$s46c`TU6V{u^ymyaR7JPsFEpd+Xx8Uu-H6u5;7kkbgj!{zW& zaGoy@=3od1W;2-#7-iy0n91NX8A>Gw$MKXA6btkOs7f?_q*oRy3{W9Tl+WQP88C~5 zvtTx#rG$A(1|L>%aix;YWMK%7SgA0DAWW-QqadAx8coINI!&r|!6LXISS}G#nY2Nu zbw-|!l1g9z&H$mov?jwFa2%n=V@cGaCxgpoviVFd!tmuWcpUy4&_Y~q0JUggWgs*b z+qz;&i~xiKq(v>23J9$8AR0la9!E*7K2EDm7g8-QKo-m4WhppN7)qidl*9ok!t@m& zECCaVV;}-HTj0xw5v~9kf!AV$GV_0-EyoA)A53~UVF3GQTBiojR4l$?a5OkfC#;7F zf~?0vfGP$pv-%0R2A32nRFjH>{xMXrujh&1C;Gu}G0_LDN7i5snhj|!y z0;R!4SV|ts_EnB%H)xfl3Dx7lsX#}d6(~@vR?wWGRQ+i*-lW1Uc>t2Z2n!yP49Lfz z(uabjTRO(;X#MH`rH8*2Frvi(yTLiox*f$R{D;~ zH73_vDezX{vFaL=>#Y=cEAUu#{omwrc=IuZYrq?j34AK8UNf%%d}i6EM9vpcCk>SQV55MOEr1z4P5caX_u(=4$ce?zC+%b!OaL;{Ej^bjRU_^}_9+ z9!d08%F)2WH3>&jJ5PwI{~qYLlf2Z_{7q8xPX4#7-cpvK`*OI?L7$IiX3Mj0V^m>9 z#OB!{J{~sSl7R!1nia79VkJ|R=pIpS_J46B% zG0v;F_bMsQiCXhDy38;0VB@;QOY(8sBF_$}jD6+e6vLYShJsetjpp-sLCw{DM=9CU zy5*t#mrve(#$55moGTGdb>^nK|AGeiT276s=|ahZCI{cnNY#d!4y}=0)5n9a`+<3;N);?a9)H-v6odV;#JeZcax!}`OPvNnr*XzUG zgZhpZ)~A*iv~=1`?%aE_Ty*@IAH03~j4gdJs)~7?Dh@?(KigdF93+b=8h0<#gE;BL zYmgXdw(Zd_x7N*-dU}K;radrJZ=f}8>8T?3lWDDC-8=7J?yHD@Vr)YT>`I>$UuQ?g zCe_?G9`7l}&UZI1@5~h&b3Q+vSI4*@BdaR!MPM~%=;C9_S?U3v7Te`$lA9J!wCH?e(dZ`I)?Z3}ZJ zL{&WX%Wv<>c3ZnmqRn;4sw!!IxvQ;u)}x>LULAW{vipl0#D`a8Kc+>6pRm1N-}2yt jua;pYm#(&KMf&a98~Yz>TKe5VZBis+ndorHvYcN541DLG delta 1456 zcmV;h1yB0yB)SWb9Df0d3;~(|00n_bL_t(o!?l-dY!p=#$Nz09?e;-iN_mJ1WobbR z3ZVs+K#X9l2`I)OXiNxFlo&K37Db3JLe$de2cjrSh(It%06{F>1w{YBeHf{*q3<2*AtQf4s&-m0MsH$ zEBv51Nj=s=A%6hN5SilxtJ4vKx+54&NFdoOI0?KDfX7M?P{JetrXc2d48>WX#MreE z;5FlU*ru=q8cG-&sd)G_&In1GW>w_dcHXZTgsWQ7C4SEO7U-b&?_r8Q>J~QBv!R}F zN%2Y2n&3FwD@nz#HHA!$G86dKvheINrmjCiabG|YzJDNKIt>8aY1wI7PT<|;m6TXv zn%>XHVGHt+#;{dkD|hWP)qyB_83TM2Tv9;t&Z)ZrNd4B!XbZrS5kbJc)?>&xY?@7^ z$^j5@Zdpg{OKN69Y=9(XzO>sHZAn5Two1*O*2UFC|RABn_Qba zZyewEIDf!9|0GPe&BrO%!8&t$qvf1$KcilO`TWye+N;`fk0&pUxbD{^<9q(J;EczX zQx}+hHs_#5L>doHDq1`E@8U~SJ)>F+>VMtSVmWrDEOj?jUM7Zo<{NaT&x>Z~E=bEe zcPz|ear;dlM7kcCd0^J53sf_X#axW){tPpT3zn22>BE~NZH^(vtu`GeCOy~x#I{Rz zlNJ_mC*RrTwv%VQXQh&qIc0H#`$M=8`Hhl0bMFHu&&G-dyBWiq%p)-}K}scQRDWV_ zv$aheVYs0mxm%coj{`wU^_3U|=B`xxU;X3K1L?JT?0?+@K!|MWVrmC=;rjX@CoW3kMZ zA^8ZAy52^R{+-YG!J5q^YP&$t9F`&J8*KzV4t3ZZZJ>~XP7}Bs<}$a~2!Aqa{0?XE zIn#rG0x`d*9iE=s?Zy5_q8@SwD=B2defgs+OvRoORZIf|*N-|pB^7%VOka|MJUG2vM-PI3W zJQal?t|RF~^GD*^Bq486{wZIZECflX^q~F+xjEx2t=17.0.0" } }, + "node_modules/react-helmet": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", + "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", + "dependencies": { + "object-assign": "^4.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.1.1", + "react-side-effect": "^2.1.0" + }, + "peerDependencies": { + "react": ">=16.3.0" + } + }, "node_modules/react-helmet-async": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz", @@ -11494,6 +11509,14 @@ "react": "^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-side-effect": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz", + "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==", + "peerDependencies": { + "react": "^16.3.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-spinners": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.15.0.tgz", diff --git a/src/client/package.json b/src/client/package.json index 7e41ecf..0dff670 100644 --- a/src/client/package.json +++ b/src/client/package.json @@ -33,6 +33,7 @@ "react": "18.3.1", "react-dom": "18.3.1", "react-fontawesome": "^1.7.1", + "react-helmet": "^6.1.0", "react-native": "0.76.7", "react-native-gesture-handler": "~2.20.2", "react-native-markdown-display": "^7.0.2",