Open3

最新技術を調べる

tadokunotadokuno

XでAI関係の書き込みを継続している人が、比較的最新の技術についてキーワードを出している。

  1. Claude Artifact、Create、Dify
  2. Next.js、vercel、supabase、Docker、k8sなど
  3. Cursor
  4. Streamlit,FastAPI
  5. Azure OpenAI Service, Be4drock, GCP VertexAI

#ノーコードで検索
7. Canva
8. AppSheet
9.Reckoner
Bubble
SunoAI
Auth0

#ローコードで検索
Copilot Studio
OutSystems
DataRobot
FlutterFlow
Power Apps
Kintone
Notion
GAS

#Ycombinator.com で探す
VectorShift
Freestyle
Zenbase AI

tadokunotadokuno

Freestyle

Freestyle.sh San Francisco, CA, USA
2024創業のスタートアップ
5人の会社
製品も洗練されていないので、まだこれからだと思われる。

TypeScript専用のプラットフォームを提供しているが、提供形態はよく分からないので、実際に使って見て評価する。

freestyle.sh から Start Building をクリックして、Freestyle Docsに飛ぶ。
ドキュメントの指示の通りに操作する。

GitHubのリポジトリを作成する。
ローカルに展開して、インストールする。
Node.js は、19以上でないといけないようなので、自分の環境のNode.jsをアップグレードする。

https://learn.microsoft.com/ja-jp/windows/dev-environment/javascript/nodejs-on-wsl
v20.16.0にアップグレード

ここに展開する。

git clone https://github.com/tadokuno/freestyle-template.git
npm i
npx freestyle dev

サンプルをビルドして、サーバーへデプロイする。
npx freestyle build
npx freestyle login
npx freestyle deploy

ここで、サブドメインをtadokuno-sample とすると、
https://tadokuno-sample.freestyle.dev として、公開される。

この辺でようやく意味が分かってきたが、要するにローカルで開発したReactアプリをほとんどそのまま公開することができるツールのようだ。

サポートしているフレームワークは、
Astro,React Native, Solid Start, SvelteKit, React となっている。

あと、便利なモジュールも提供されているようだが、内容は不明。
Cloudstate, Image & Blob Storage, TanStack Passkey auth

もちろん、Freestyleのフレームワークを使って、開発をすることもできる。ChatGPTがそれに対応できるのかが分からないが、挑戦してみたい。

最初のテンプレで、GitHubとの連携を設定する。自分のリポジトリとして生成されたものは、Astro によるテンプレートとなる。

次に、React nativeというサンプルを使って見る。

https://github.com/freestyle-sh/freestyle-react-native-template.git

ローカルにクローンして、ディレクトリを移動し
npm i freestyle-sh

とするが、warningが出る。脆弱性の問題のよう。
npm audit fix

実行する。
npm start

Metro waiting on exp://172.17.48.9:8081
Web is waiting on http://localhost:8081

Metroってなんだ?

Webでローカルにアクセスすると、ビルドが始まってしばらく時間が掛かる。
こんなメッセージが、
λ Bundled 9893ms node_modules/expo-router/node/render.js (889 modules)
Web Bundled 9971ms node_modules/expo-router/entry.js (888 modules)
λ WARN props.pointerEvents is deprecated. Use style.pointerEvents
λ WARN Button is deprecated. Please use Pressable.
λ WARN TouchableOpacity is deprecated. Please use Pressable.
λ WARN accessibilityRole is deprecated. Use role.
λ WARN focusable is deprecated.
Web Bundled 36ms node_modules/expo-router/entry.js (1 module)
λ Bundled 22ms node_modules/expo-router/node/render.js (1 module)
LOG [web] Logs will appear in the browser console
Web Bundled 196ms node_modules/expo-router/entry.js (1 module)
λ WARN props.pointerEvents is deprecated. Use style.pointerEvents
"shadow*" style props are deprecated. Use "boxShadow".
λ WARN accessibilityRole is deprecated. Use role.

古いモジュールを使っている。どうすればいいのかちょっと分からない。

tadokunotadokuno

ローカルでテストしていたが、肝心のCloudstateが働かないのが判明した。
しばらく悩んでいたが、結局ローカルでは動かず、本番サーバーにデプロイしなければ正しく動かないということが判明した。
色々やっている際に、ディレクトリを削除してしまい、それをデプロイしたサーバーからサブドメインを削除できなくなってしまった。

やむを得ず、継続する。

ローカル環境で確認できるのは、UIぐらいか。
freestypeのReact Nativeのサンプルコードに手を加えることで、自分のアプリを仕上げることにする。

試しに、Numbers APIを使ってみようとしたが、サーバーからはhttpsのリクエストしか許さないらしくエラーになった。Numbers APIはhttpしかない。

ということで、miiboを使って占いアプリを作る。

/app/(tabs)/index.tsx を修正することで、簡単なアプリを作ることができる。

また、占いの履歴は残るようにしたいので、Cloudstateを使って永続オブジェクトとする。

サンプルプログラムに対する修正箇所は2つで、
/cloudstate/history.ts
/app/(tabs)/index.tsx

/cloudstate/history.ts

// src/cloudstate/history.ts

import { cloudstate } from 'freestyle-sh';

@cloudstate
class History {
  static id = "history";
  entries: Array<{ number: string, fact: string }> = [];

  addEntry(entry: { number: string, fact: string }) {
    this.entries.push(entry);
console.log(entry);
  }

  getEntries() {
    return this.entries;
  }
}

export const HistoryCS = History;

/app/(tabs)/index.tsx

import React, { useState } from 'react';
import {
  StyleSheet,
  TextInput,
  View,
  Text,
  Button,
} from "react-native";
import axios from 'axios';
import { useCloud } from "freestyle-sh";
import { HistoryCS } from "@/cloudstate/history";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";

export default function HomeScreen() {
  const history = useCloud<typeof HistoryCS>("history");
  const client = useQueryClient();
  const [date, setDate] = useState('');
  const [response, setResponse] = useState('');

  const fetchFortune = async () => {
    try {
      const apiRequest = {
        api_key: "MIIBO_API_KEY",
        agent_id: "MIIBO_AGENT_ID",
        utterance: date,
        uid: "MIIBO_UID"
      };
      const response = await axios.post('https://api-mebo.dev/api', apiRequest, {
        headers: { 'Content-Type': 'application/json' }
      });
      const fortune = response.data.bestResponse.utterance;
      setResponse(fortune);
      await history.addEntry({ date, response: fortune });
      client.invalidateQueries({
        queryKey: [history.getEntries],
        exact: true,
      });
    } catch (error) {
      console.error('Error fetching data from miibo API:', error);
      setResponse('Error fetching data.');
    }
  };

  const { data: historyEntries } = useQuery({
    queryKey: [history.getEntries],
    queryFn: () => history.getEntries(),
  });

  return (
    <View style={styles.container}>
      <Text style={styles.title}>誕生日占い</Text>

      <TextInput
        style={styles.input}
        placeholder="Enter a date (e.g., 3月3日)"
        value={date}
        onChangeText={setDate}
      />
      <Button
        onPress={fetchFortune}
        title="Get Fortune"
      />
      {response ? <Text style={styles.response}>{response}</Text> : null}

      <View style={styles.historyContainer}>
        <Text style={styles.historyTitle}>History</Text>
        {historyEntries && historyEntries.map((item, index) => (
          <View key={index} style={styles.historyItem}>
            <Text>{item.date}</Text>
            <Text>{item.response}</Text>
          </View>
        ))}
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    padding: 16,
  },
  title: {
    fontSize: 32,  // H1のサイズに相当
    fontWeight: 'bold',
    textAlign: 'center',
    marginBottom: 20,
  },
  input: {
    height: 40,
    borderColor: 'gray',
    borderWidth: 1,
    marginBottom: 12,
    paddingHorizontal: 8,
  },
  response: {
    marginTop: 12,
    fontSize: 16,
  },
  historyContainer: {
    marginTop: 16,
  },
  historyTitle: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 8,
  },
  historyItem: {
    marginBottom: 8,
  },
});

miibo側で、日付を入力して占いを出力するAPIを準備しておく。
使用する生成AIは、GPT-4o とする。

これで、UIや、API呼び出しが上手くいくかどうかはわかる。
履歴が保持されるかどうかは、デプロイしてからテストする。

npx freestyle build
npx freestyle deploy

サブドメインを指定して、実行可能となる。