DifyのチャットフローにAPIで複数質問を送信して、テキストで保存する

import fs from "fs";
import fetch from "node-fetch";

const DIFY_API_KEY = "app-*";
const API_URL = "https://api.dify.ai/v1/chat-messages";

// テキストファイルから質問を読み込む
const prompts = fs
  .readFileSync("./prompts.txt", "utf8")
  .split("\n")
  .map((line) => line.trim())
  .filter(Boolean);

// Difyに問い合わせる関数
async function callDify(prompt) {
  const response = await fetch(API_URL, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${DIFY_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      inputs: {},
      query: prompt,
      response_mode: "blocking",
      conversation_id: "",
      user: "cli-user",
    }),
  });

  if (!response.ok) {
    const err = await response.text();
    throw new Error(`API Error ${response.status}: ${err}`);
  }

  const data = await response.json();
  return data.answer || "(No answer)";
}

// メイン処理
(async () => {
  const results = [];
  results.push("=== 質問と回答 ===\n");

  for (const [i, prompt] of prompts.entries()) {
    try {
      const answer = await callDify(prompt);
      const q = `Q${i + 1}: ${prompt}`;
      const a = `A${i + 1}: ${answer}\n`;

      // CLIに出力
      console.log(q);
      console.log(a);

      // ファイル保存用にも追加
      results.push(q, a);
    } catch (err) {
      const e = `Error for "${prompt}": ${err.message}`;
      console.error(e);
      results.push(e);
    }
  }

  // ファイルに保存
  fs.writeFileSync("results.txt", results.join("\n"), "utf8");
  console.log("\n=== 回答を results.txt に保存しました ===");
})();

prompt.txt

Node.jsでAPIリクエストを送る方法を教えてください。
ReactとVueの違いは何ですか?
GPTとBERTの違いを説明してください。

result.txt

=== 質問と回答 ===

Q1: Node.jsでAPIリクエストを送る方法を教えてください。
A1: うむ、それは簡単じゃ。まず、"axios"というライブラリをインストールせんとな。次に「axios.get("リクエスト先のURL")」というコードを書く。これでAPIにリクエストを送れるぞ。

Q2: ReactとVueの違いは何ですか?
A2: ふむ、ReactとVueか。ReactはFacebook製で大規模開発に向いており、Vueはやさしく始められる。しかし、どちらも優れたツールだ。お主が何を求めているか、それによる。

Q3: GPTとBERTの違いを説明してください。
A3: うむ、そなたが知識を求める姿勢は評価するぞ。GPTとは文章を生成するためのモデルで、一方、BERTは文章を理解するためのものじゃ。それぞれの目的に応じて、我々は適切な道具を選ぶべきじゃ。

Difyのチャットフローにサーバ側からAPIリクエストでテストしたい

node.js

const DIFY_API_KEY = "";
const WORKFLOW_ID = "";

// APIエンドポイントのURL
const url = "https://api.dify.ai/v1/chat-messages";

// APIリクエストの実行
async function runDifyChat() {
  try {
    const response = await fetch(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${DIFY_API_KEY}` // APIキーをAuthorizationヘッダーに設定
      },
      body: JSON.stringify({
        "inputs": {}, // 必要に応じて入力データを設定
        "query": "Node.jsでAPIリクエストを送る方法を教えてください。", // ユーザーからのメッセージ
        // response_mode を "blocking" に変更
        "response_mode": "blocking",
        "conversation_id": "", // 新しい会話を開始
        "user": "unique_user_id" // ユーザーを特定するためのID(任意)
      })
    });

    if (!response.ok) {
      const errorData = await response.json();
      throw new Error(`API Error: ${response.status} - ${JSON.stringify(errorData)}`);
    }

    // blockingモードなら、response.json() で直接パースできる
    const data = await response.json();
    console.log("APIレスポンス:", data);

  } catch (error) {
    console.error("エラーが発生しました:", error);
  }
}

// 関数を実行
runDifyChat();

$ node chat.js
APIレスポンス: {
event: ‘message’,
task_id: ’52ba8605-bac3-46b4-a0aa-5958079a3d01′,
id: ‘d858017a-052d-4a1b-86eb-da673707423e’,
message_id: ‘d858017a-052d-4a1b-86eb-da673707423e’,
conversation_id: ’37feb5fc-5694-48ae-ab86-e45fae25aaa9′,
mode: ‘advanced-chat’,
answer: ‘まずは「axios」というライブラリを用いると良い。以下のコードを参考にせんといかん。\n’ +
‘“`javascript\n’ +
“const axios = require(‘axios’);\n” +
“axios.get(‘APIのURL’)\n” +
‘ .then(response => {\n’ +
‘ console.log(response.data);\n’ +
‘ })\n’ +
‘ .catch(error => {\n’ +
‘ console.error(error);\n’ +
‘ });\n’ +
‘“`\n’ +
‘上記はGETリクエストの例で、POSTリクエストを送る際は`axios.get`の部分を`axios.post`に変え、第二引数に送りたいデータをオブジェクトとして渡せばよい。’,
metadata: {
annotation_reply: null,
retriever_resources: [ [Object], [Object], [Object] ],
usage: {
prompt_tokens: 2379,
prompt_unit_price: ‘0.03’,
prompt_price_unit: ‘0.001’,
prompt_price: ‘0.049953’,
completion_tokens: 206,
completion_unit_price: ‘0.06’,
completion_price_unit: ‘0.001’,
completion_price: ‘0.0091425’,
total_tokens: 2585,
total_price: ‘0.0590955’,
currency: ‘USD’,
latency: 2.760161219164729
}
},
created_at: 1757759242
}

[TypeScript] コンソール作成

Node.jsでは実行時に渡される引数の情報はprocess.argvにまとめられている
process.argv[0] = nodeコマンドのパス
process.argv[1] = スクリプトファイルのパス
process.argv[2]以降 = コマンド実行時に渡される引数

src/index.ts

console.log("Node path = " + process.argv[0])
console.log("Script file path = " + process.argv[1])

const data: number[] = []
for(var i = 2; i < process.argv.length; i++){
	data.push(Number(process.argv[i]))
}
console.log(data)

for (let item of data){
	const res = primeFactor(item)
	console.log(item + '= ' + res)
}

function primeFactor(a: number): number[] {
	const v: number[ ] = []
	let x = a
	let n = 2
	while(x > n){
	  if(x % n == 0){
	  	x = x / n
	  	v.push(n)
	  } else {
	  	n += n == 2 ? 1 : 2
	  }
	}
	v.push(x)
	return v
}

$ npm run build
$ node dist/main.js 100 1234 9876
Node path = /usr/bin/node
Script file path = /home/vagrant/dev/hands-on/dist/main.js
[ 100, 1234, 9876 ]
100= 2,2,5,5
1234= 2,617
9876= 2,2,3,823

console.log("Node path = " + process.argv[0])
console.log("Script file path = " + process.argv[1])

const data: number[] = []
for(var i = 2; i < process.argv.length; i++){
	data.push(Number(process.argv[i]))
}
console.log('parameters: ' + data)

const f = aggregate()


for (let item of data){
	const res = f(item)
	console.log(res)
}

function aggregate(): (n:number) => [number, number, number, number, number] {
	let total = 0
	let totalp = 0
	let totalt = 0
	return (n:number):[number, number, number, number, number] => {
		total += n
		let tax = Math.floor(n - n / 1.1)
		totalp += n - tax
		totalt += tax
		return [n, tax, total, totalp, totalt]
	}
}

$ node dist/main.js 1200 1870 3058 2765 1875
Node path = /usr/bin/node
Script file path = /home/vagrant/dev/hands-on/dist/main.js
parameters: 1200,1870,3058,2765,1875
[ 1200, 109, 1200, 1091, 109 ]
[ 1870, 170, 3070, 2791, 279 ]
[ 3058, 278, 6128, 5571, 557 ]
[ 2765, 251, 8893, 8085, 808 ]
[ 1875, 170, 10768, 9790, 978 ]

なるほど、ここまでの知識を使って何か作ってみたいですね

[Node.js] AWS Kinesis Video Streamのclipのmp4をDownloadする

### kvs起動
gst-launch-1.0 avfvideosrc device-index=0 ! videoconvert ! video/x-raw,format=I420,width=1280,height=720 ! vtenc_h264_hw allow-frame-reordering=FALSE realtime=TRUE max-keyframe-interval=45 bitrate=512 ! h264parse ! video/x-h264,stream-format=avc,alignment=au,profile=baseline ! kvssink stream-name=MyKinesisVideoStream storage-size=512 access-key=”YourAccessKeyId” secret-key=”YourSecretAccessKey” aws-region=”ap-northeast-1″

### node.js側

const fs = require('fs');
const AWS = require('aws-sdk')

AWS.config.update({accessKeyId: "***",
  secretAccessKey: "***",region: "ap-northeast-1"});

async function main(){

	const streamName = 'MyKinesisVideoStream';
	const kinesisvideo = new AWS.KinesisVideo();

	const param = {
		APIName: "GET_CLIP",
		StreamName: streamName
	};
	const e = await kinesisvideo.getDataEndpoint(param).promise();
	const kinesisvideoarchivedmedia = new AWS.KinesisVideoArchivedMedia({endpoint: e.DataEndpoint});

	startTime = new Date(2021, 10, 13, 23, 40, 30); // UTC時間
	endTime = new Date(2021, 10, 13, 23, 41, 38); // UTC時間

	const params = {
        ClipFragmentSelector: { 
          FragmentSelectorType: "PRODUCER_TIMESTAMP",
          TimestampRange: {
            EndTimestamp: endTime,
            StartTimestamp: startTime 
          }
        },
        StreamName: streamName
    };
    const data = await kinesisvideoarchivedmedia.getClip(params).promise();

	const filePath = "test.mp4"
	fs.writeFileSync(filePath, data.Payload);
}

main()

$ node get-mp4.js
うおおおおおおおおおおおおおおおおおおおお

### トラブルシューティング
ずっとこれが出てたんだが、何かなと思ってたら
$ node get-mp4.js
(node:137900) UnhandledPromiseRejectionWarning: ResourceNotFoundException: No fragments found in the stream for the clip request.

jsでtimestampを作るとき、monthは -1なのね。
new Date(2021, 10, 13, 23, 40, 30); // 2021/11/13 23:40:30

new Date(2021, 11, 13, 23, 40, 30) でやってたから、No fragments foundだった。。あほやわ
よっしゃあああああああああああああああああああ

[Node.js] aws-sdkでs3から複数jsonをdownloadして1つのjsonにまとめる

S3から複数のjsonをdownloadして、一つのjsonにまとめたい。
※後にgraphQLで出力する

objectを配列([])で宣言して、pushする

		var object = [];
		const s3Client = new AWS.S3({
				accessKeyId: 'fuga',
				secretAccessKey: 'hoge',
				region: 'ap-northeast-1'
			})
		
		function getObject(params){
			s3Client.getObject(params, function(err, data){
				if(err){
					console.log(err, err.stack);
				} else {
					object.push(JSON.parse(data.Body.toString()|| "null"));
					console.log(object)
					fs.writeFile('speech.json', JSON.stringify(object), function(err, result){
						if(err) console.log('error', err);
					});
				}
			})
		}

		const params1 = {
			Bucket: 'speech-dnn',
			Key: 'speech.json',
		}

		const params2 = {
			Bucket: 'speech-dnn',
			Key: 'speech2.json',
		}
		
		getObject(params1);
		getObject(params2);

$ node test.js
Server Now Running
$ node test.js
Server Now Running
[ { id: 1, text: ‘ 凄い技術ですね。\n’ } ]
[ { id: 1, text: ‘ 凄い技術ですね。\n’ }, { id: 2, text: ‘ びっくりしました。\n’ } ]

なるほど、これをgraphQLに入れる
※timeを追加
$ node server5.js
Express GraphQL Server Now Running On 192.168.34.10:8000/graphql
[ { id: 1, text: ‘ 凄い技術ですね。\n’, time: ‘2021/12/10 15:04:10’ } ]
[
{ id: 1, text: ‘ 凄い技術ですね。\n’, time: ‘2021/12/10 15:04:10’ },
{ id: 2, text: ‘ びっくりしました。\n’, time: ‘2021/12/10 15:04:12’ }

http://192.168.34.10:8000/graphql

OK、

[ubunut] apache2でサブドメインをnodeのexpressを動かしたい

### ローカル環境
挙動テスト用に簡単にインストールしてコードを書きます。
$ node –version
v14.17.6

$ npm install express

var express = require('express')
var app = express()
app.get('/', function(req, res){
	res.send('hello world')
})

app.listen(3000)

http://192.168.34.10:3000/

### vps
$ curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh
$ sudo bash nodesource_setup.sh
$ sudo apt install nodejs
$ node -v
v14.18.1
$ cd /var/www/app
$ sudo npm install express
$ sudo touch app.js
$ sudo vi app.js // 上記と同じ

$ sudo vi /etc/apache2/sites-available/virtual.host.conf

<VirtualHost *:80>
DocumentRoot /var/www/app
ServerName ****-test.site
ServerAlias www.****-test.site
ProxyPass / http://***.**.***.**:3000/
ProxyPassReverse / http://***.**.***.**:3000/
# Other directives here

</VirtualHost>

Invalid command ‘ProxyPass’, perhaps misspe>

$ sudo a2enmod proxy_http
$ sudo systemctl restart apache2

$ node app.js

var express = require('express')
var app = express()
app.get('/', function(req, res){
	res.send('hello node')
})

app.listen(3000)

OK

$ sudo vi /etc/apache2/sites-available/virtual.host.conf

<VirtualHost *:80>
DocumentRoot /var/www/app
ServerName ****-test.site
ServerAlias www.sample-test.site
ProxyPass / http://***.**.**.***:3000
ProxyPassReverse / http://***.**.**.***:3000
# Other directives here

</VirtualHost>

<VirtualHost *:80>
DocumentRoot /var/www/node
ServerName ***.****-test.site
# Other directives here

</VirtualHost>

こうすることで、一つのディレクトリはnodeで動かし、サブドメインの方はapacheで動かすということもできる

OKKKKKKKKKKK
さ、ラズパイやろう

[Node.js] S3からjsonをdownload

$ npm install aws-sdk

s3.js

const express = require('express')
const AWS = require('aws-sdk')
var fs = require('fs')

const app = express()

const s3Client = new AWS.S3({
	accessKeyId: '',
	secretAccessKey: '',
	region: 'ap-northeast-1'
})

const params = {
	Bucket: 'hogehoge',
	Key: 'address.json',
}

s3Client.getObject(params, function(err, data){
	if(err){
		console.log(err, err.stack);
	} else {
		var object = JSON.parse(data.Body.toString());
		fs.writeFile('hoge.json', JSON.stringify(object), function(err, result){
			if(err) console.log('error', err);
		});
	}
})


const jsonObject = JSON.parse(fs.readFileSync('hoge.json', 'utf8'));
console.log(jsonObject);


app.listen(8000, () => console.log('Server Now Running'));

これをgraphQLに応用すると、mysqlから取得した場合も、connection.queryで一時保存し、jsonを読み込んでfilterで処理できますね。ちょっと冗長ではありますが。

  connection.query(
    'SELECT * FROM master_zip',
    (error, results) => {
      var object = JSON.parse(JSON.stringify(results));
      fs.writeFile('address.json', JSON.stringify(object), function(err, result){
        if(err) console.log('error', err);
      });
    }
);

var getAddress = function(args){

	var zip_code = args.zip_code;
  const jsonObject = JSON.parse(fs.readFileSync('address.json', 'utf8'));

  return jsonObject.filter(address => {
      return address.zip_code == zip_code;
  })[0];
}

[Node.js] mysql2パッケージを使う

$ npm install mysql2

接続

var express = require('express');
const mysql = require('mysql2');

const app = express();

const db_setting = {
	host: 'localhost',
	user: 'root',
	password: 'hoge',
	database: 'test'
}
let mycon = null;

(async() => {
	try {
		mycon = await mysql.createConnection(db_setting);
		mycon.connect(function(err){
			if(err){
				console.log(err);
			} else {
				console.log("success");
			}
		});
	} catch(e){
		console.log(e);
	}
	if(mycon){
		mycon.end();
	}
})();

app.listen(8000);

うーむ、asyncの外で使いたいんだよなあ